当前位置:   article > 正文

数据结构(Java):力扣单链表面试OJ题_单链表逆置力扣题

单链表逆置力扣题

1、题一:获取链表倒数第k个节点

. - 力扣(LeetCode)

1.1 思路解析

此题我们使用双指针法求解。

首先,我们要知道,倒数的第k个节点,距离倒数第一个节点还需要移动k-1次

1.那么我们可以定义出两个指针,分别为fast和slow,他们初始均值均为头结点head

2.先让fast指针向后移动k-1次,slow指针保持不动。

3.接着,fast指针和slow指针同步后移直至fast指针指向最后一个节点。

4.此时,slow指针所指向的位置就是倒数第k个节点。

原理:这个解题思路的原理就是,fast指针和slow指针始终保持着k-1个移动次数,而当fast指针指向最后一个节点(即倒数第一个节点)时,那么slow指针指向的就是倒数第k个节点。

注意事项:

1.k的值是否合法 2.头指针head是否为null

1.2 代码

  1. public int kthToLast(ListNode head, int k) {
  2. //当头指针为空时
  3. if (head == null) {
  4. return Integer.MAX_VALUE;
  5. }
  6. //当k的值<=0时
  7. ListNode cur = head;
  8. int size = 0;
  9. while (cur != null) {
  10. size++;
  11. cur = cur.next;
  12. }
  13. if (k <= 0) {
  14. return Integer.MAX_VALUE;
  15. }
  16. //双指针法求解
  17. ListNode fast = head;
  18. ListNode slow = head;
  19. //先让fast指针移动k-1次
  20. int count = 0;
  21. while (count != k - 1) {
  22. fast = fast.next;
  23. //当k值>size时(不合法),fast指针移动的k-1次中,必然会指向null
  24. if (fast == null) {
  25. return Integer.MAX_VALUE;
  26. }
  27. count++;
  28. }
  29. //slow和fast同步后移,直至fast指向倒数第一个元素(slow和fast始终保持k-1个移动次数)
  30. while (fast.next != null) {
  31. fast = fast.next;
  32. slow = slow.next;
  33. }
  34. //返回val
  35. return slow.val;
  36. }

2、题二:逆置单链表

. - 力扣(LeetCode)

2.1 思路解析

此题我们使用头插法求解。

这道题的思路很简单:从第二个节点开始,每得到一个节点,将此节点进行头插操作。

注意:要将最开始的头结点的next置为null(因为链表的头节点在逆置后就变成了尾结点)

2.2 代码

  1. public ListNode reverseList(ListNode head) {
  2. //当头结点head为空时
  3. if (head == null) {
  4. return head;
  5. }
  6. //获取第二个节点,将第一个节点的next置为null
  7. ListNode cur = head.next;
  8. head.next = null;
  9. //头插
  10. while (cur != null) {
  11. //先获取当前节点的下一个节点
  12. ListNode curN = cur.next;
  13. //将当前节点的next指向头结点(头插)
  14. cur.next = head;
  15. //将head更新为新头插的节点
  16. head = cur;
  17. //更新cur,继续头插
  18. cur = curN;
  19. }
  20. //返回逆置后的头结点
  21. return head;
  22. }

3、题三:移除链表元素(删除所有某一数值的节点,且一次循环)

. - 力扣(LeetCode)

3.1 思路解析

此题我们使用双指针法求解。

1.我们定义两个指针分别为prev(初始指向head头结点 即第一个节点)和cur(初始指向head的next节点 即第二个节点)。

2.遍历链表。

3.当cur所指节点的值为所要删除的val值时,cur向后移动,prev不动,且将prev的next指针指向移动后的cur,完成节点的删除。

4.若cur所指节点值不是所要删除的val值时,cur和prev同步后移一位。

5.当cur指向null时,遍历完成。

6.经过上述操作,除第一个节点外,其余的节点均已删除完成,我们只需额外判断第一个节点的值是否为要删除的val值即可。

3.2 代码

  1. public ListNode removeElements(ListNode head, int val) {
  2. //当head为空时
  3. if(head == null) {
  4. return head;
  5. }
  6. //双指针法求解
  7. ListNode prev = head;
  8. ListNode cur = head.next;
  9. while (cur != null) {
  10. //判断当前节点的值是否为要删除的val值
  11. if (cur.val == val) {//若是,删除节点
  12. cur = cur.next;
  13. prev.next = cur;
  14. }else {//若不是,均向后移动
  15. cur = cur.next;
  16. prev = prev.next;
  17. }
  18. }
  19. //判断第一个节点的值是否我要删除的val值。
  20. if (head.val == val) {
  21. head = head.next;
  22. }
  23. //返回删除后的头结点
  24. return head;
  25. }

4、题四:获取链表的中间节点

. - 力扣(LeetCode)

 4.1 思路解析

此题我们使用快慢指针法求解。

1.首先,定义两个指针分别为fast和slow。

2.fast指针,每次向后移两位;slow指针,每次向后移一位。

3.链表元素个数为奇数时,当fast.next == null时,slow指向的节点就是中间节点;链表元素个数为偶数时,当fast == null时,slow指向的节点就是中间节点。

注意:

循环结束条件一定要写为:

while(fast != null && fast.next != null)

原因:

1.两者只要有一个不满足就要结束循环,说明已经找到了中间节点

2.一定要fast != null在前,避免fast.next时出现空指针异常

4.2 代码

  1. public ListNode middleNode(ListNode head) {
  2. //快慢指针法求解
  3. ListNode slow = head;
  4. ListNode fast = head;
  5. //找中间节点
  6. //一定要fast != null在前,避免出现空指针异常
  7. //两个条件只要有一个不满足就结束循环,说明到了中间节点的位置
  8. while (fast != null && fast.next != null) {
  9. ListNode fastN = fast.next;
  10. fast = fastN.next;
  11. slow = slow.next;
  12. }
  13. //返回中间节点
  14. return slow;
  15. }

5、题五:分割链表

给定 x, 把一个链表整理成前半部分小于 x, 后半部分大于等于 x 的形式

链表分割_牛客题霸_牛客网

5.1 思路解析

1.将链表进行遍历

2.将val < x的节点添加到新链表head1中

3.将val >= x的节点添加到新链表head2中

4.将head1和head2进行拼接

5.返回拼接后新表的头结点head1

注意:

1.无论如何,一定要将head2的最后一个节点next域置为null

2.当表中所以元素的val值都大于等于x时,那么head1将为空表,直接返回head2即可

5.2 代码

  1. public ListNode partition(ListNode pHead, int x) {
  2. //当pHead为空时
  3. if (pHead == null) {
  4. return null;
  5. }
  6. ListNode cur = pHead;
  7. ListNode prev = null;
  8. ListNode head1 = null;
  9. ListNode head2 = null;
  10. ListNode cur1 = null;
  11. ListNode cur2 = null;
  12. //遍历链表 将val < x的节点添加到新表head1中
  13. //将val >= x的节点添加到新表head2中
  14. while (cur != null) {
  15. if (cur.val < x) {
  16. if (head1 == null) {
  17. head1 = cur1 = cur;
  18. }else {
  19. cur1.next = cur;
  20. cur1 = cur1.next;
  21. }
  22. }else {
  23. if (head2 == null) {
  24. head2 = cur2 = cur;
  25. }else {
  26. cur2.next = cur;
  27. cur2 = cur2.next;
  28. }
  29. }
  30. cur = cur.next;
  31. }
  32. //注意:需要将head2的最后一个节点的next置为null
  33. if(head2 != null) {
  34. cur2.next = null;
  35. }
  36. //如果旧表中所有元素的val值都大于x,那么head1将会是空表,直接返回head2即可
  37. if (head1 == null) {
  38. return head2;
  39. }
  40. //将head1和head2进行拼接
  41. cur1.next = head2;
  42. return head1;
  43. }

 6、题六:判断链表是否回文

链表的回文结构_牛客题霸_牛客网

6.1 思路解析

回文结构,其实就是对称结构:

 

当元素个数为奇数时:

1.找到中间节点(使用上面的例题已经讲到)

2.以中间节点为头,逆置其后链表

3.逆置后,链表存在下图结构,指针从两端开始比较val值是否相等,直到相遇时结束,若均相等则为回文结构。

当元素个数为偶数时:

1.偶数时,newH和slow无法相遇

2.我们只需额外判断 当 newH.next == slow 时,说明为回文结构。

6.2 代码

  1. public boolean chkPalindrome(ListNode A) {
  2. // write code here
  3. if (A == null) {
  4. return true;
  5. }
  6. ListNode fast = A;
  7. ListNode slow = A;
  8. //找中间节点
  9. while (fast != null && fast.next != null) {
  10. fast = fast.next.next;
  11. slow = slow.next;
  12. }
  13. //逆置中间节点后的链表
  14. ListNode cur = slow.next;
  15. ListNode curN = null;
  16. while (cur != null) {
  17. curN = cur.next;
  18. cur.next = slow;
  19. slow = cur;
  20. cur = curN;
  21. }
  22. //从两端开始遍历,比较值是否相等
  23. ListNode newCur = A;
  24. while (slow != newCur) {
  25. if (slow.val != newCur.val) {
  26. return false;
  27. }
  28. if (newCur.next == slow) {
  29. return true;
  30. }
  31. slow = slow.next;
  32. newCur = newCur.next;
  33. }
  34. return true;
  35. }

7、题七:相交链表(找出相交节点)

. - 力扣(LeetCode)

7.1 思路解析 

首先,我们要知道,相交链表的形状为Y形,而非X形:

1.遍历求出各链表的长度,并求得长度的差值len。

2.将较长链表的头指针向后移动len个位置。

3.两个链表的头指针同时后移,会在交点处相遇。

7.2 代码

  1. public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
  2. ListNode curA = headA;
  3. ListNode curB = headB;
  4. int lenA = 0;
  5. int lenB = 0;
  6. //求链表1的长度
  7. while (curA != null) {
  8. lenA++;
  9. curA = curA.next;//curA发生改变
  10. }
  11. //求链表2的长度
  12. while (curB != null) {
  13. lenB++;
  14. curB = curB.next;//curB发生改变
  15. }
  16. //将curA和curB重新指向链表的起始位置
  17. curA = headA;
  18. curB = headB;
  19. //差值
  20. int len = lenA - lenB;
  21. //将较长的链表头指针后移差值个长度
  22. if (len < 0) {
  23. len = -len;
  24. while (len != 0) {
  25. curB = curB.next;
  26. len--;
  27. }
  28. }else {
  29. while (len != 0) {
  30. curA = curA.next;
  31. len--;
  32. }
  33. }
  34. //相遇处即为交点
  35. while (curA != curB) {
  36. curA = curA.next;
  37. curB = curB.next;
  38. }
  39. return curA;
  40. }

8、题八:判断链表是否带环

. - 力扣(LeetCode)

8.1 思路解析 

此题我们使用快慢指针法求解。

1.定义fast为快指针,一次移动两个位置;slow为慢指针,一次移动一个位置。

2.若链表带环,那么这两个指针必定相遇(追击问题)。

注意:

因为fast一次走两步,slow一次走一步,那进入环中后,每次移动,fast和slow之间的距离必定缩短一个节点,那么必定相遇。

(若每次移动两者间缩短的距离不为1时,那么即使有环,也可能不会相遇)

8.2 代码

  1. public boolean hasCycle(ListNode head) {
  2. ListNode fast = head;
  3. ListNode slow = head;
  4. //判断是否相遇
  5. while (fast != null && fast.next != null) {
  6. fast = fast.next.next;
  7. slow = slow.next;
  8. if (fast == slow) {
  9. return true;
  10. }
  11. }
  12. return false;
  13. }

9、题九:求环的入口点

. - 力扣(LeetCode)

9.1 思路解析

设:
起点到入口点的长度为X
相遇节点到入口点长度为Y
环的长度为C

因为fast所走路程为slow所走路程的两倍
故:
X + n*C + (C - Y) = 2*(X + C - Y)
则:
X + (2-n)*C = Y
说明:相遇节点到入口点的距离和起点到入口点的距离相等
则,从相遇节点和起点开始相向而行,相遇时的节点就是入口点

注意:我们还需要处理不是环的情况!!!

9.2 代码

  1. public ListNode detectCycle(ListNode head) {
  2. //head为空时
  3. if (head == null) {
  4. return head;
  5. }
  6. ListNode fast = head;
  7. ListNode slow = head;
  8. //确定相遇时的节点
  9. while (fast != null && fast.next != null) {
  10. fast = fast.next.next;
  11. slow = slow.next;
  12. if (fast == slow) {
  13. break;
  14. }
  15. }
  16. //不是环
  17. if (fast == null || fast.next == null) {
  18. return null;
  19. }
  20. //从起点和相遇节点开始相向而行,相遇的节点就是入口点
  21. ListNode curH = head;
  22. while (curH != slow) {
  23. curH = curH.next;
  24. slow = slow.next;
  25. }
  26. return slow;
  27. }

10、题十:合并两个有序链表

. - 力扣(LeetCode)

10.1 思路解析

思路很简单,就是从各链表起点开始遍历,比较val值,谁的值小,就插入到新链表当中。

若某一链表的元素全部插入新链表当中后,将另一链表剩余元素直接拼接即可

10.2 代码

  1. public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
  2. //链表为空时
  3. if (list1 == null) {
  4. return list2;
  5. }
  6. if (list2 == null) {
  7. return list1;
  8. }
  9. ListNode cur1 = list1;
  10. ListNode cur2 = list2;
  11. ListNode newH = null;
  12. ListNode newCur = null;
  13. //从头遍历各链表 谁值小 谁就尾插到新链表中
  14. while (cur1 != null && cur2 != null) {
  15. if (cur1.val < cur2.val) {
  16. if (newH == null) {
  17. newH = newCur = cur1;
  18. }else {
  19. newCur.next = cur1;
  20. newCur = newCur.next;
  21. }
  22. cur1 = cur1.next;
  23. }else {
  24. if (newH == null) {
  25. newH = newCur = cur2;
  26. }else {
  27. newCur.next = cur2;
  28. newCur = newCur.next;
  29. }
  30. cur2 = cur2.next;
  31. }
  32. }
  33. //某一链表的元素全部插入后,将另一链表剩余元素直接拼接即可
  34. if (cur1 == null) {
  35. newCur.next = cur2;
  36. }
  37. if (cur2 == null) {
  38. newCur.next = cur1;
  39. }
  40. return newH;
  41. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/908763
推荐阅读
相关标签
  

闽ICP备14008679号