目录
说明:题目来源于牛客网
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

- public ListNode ReverseList(ListNode head) {
- if(head == null) {
- return null;
- }
- ListNode prev = null;
- ListNode cur = head;
-
- while( cur!= null){
- ListNode curNext= cur.next;
- cur.next = prev;
- prev = cur;
- cur = curNext;
- }
- return prev;
- }
时间复杂度:O(n), 遍历一次链表
空间复杂度:O(1)
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n),空间复杂度 O(1)。
例如:
给出的链表为 1→2→3→4→5→NULL, m=2,n=4
返回 1→4→3→2→5→NULL.。

思路
- public ListNode reverseBetween (ListNode head, int m, int n) {
- // write code here
- if(head == null) {
- return null;
- }
- //设置一个新的链表头
- ListNode newHead = new ListNode(-1);
- newHead.next = head;
- //前序节点
- ListNode node = newHead;
- //当前节点
- ListNode cur = head;
- //放入M前面的节点
- for (int i = 0; i < m-1; i++) {
- node = cur;
- cur = cur.next;
- }
- //反转m 到n
- for(int i = m; i < n; i++) {
- //标记好节点
- ListNode tmp = cur.next;
- cur.next = tmp.next;
-
-
- tmp.next = node.next;
- node.next = tmp;
- }
- return newHead.next;
- }
复杂度:
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表。如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样。你不能更改节点中的值,只能更改节点本身。

思路:
- public ListNode reverseKGroup (ListNode head, int k) {
- // write code here
- //连接K前面的节点
- ListNode tail = head;
- for(int i = 0; i < k; i++) {
- if (tail == null) return head;
- tail = tail.next;
- }
- //到这一步,tail就走到了下一组的头结点
- //到达K,翻转
- ListNode pre = null;//遍历的前序节点
- ListNode cur = head;//原本的头不动,用一个新的来记录
- while(cur != tail) {
- ListNode curNext = cur.next;
- cur.next = pre;
- pre = cur;
- cur = curNext;
- }
- //到这里就翻转完一组K
- //递归下一组
- //原本的头节点因为不动,还在原来的位置,现在成了尾结点
- //尾结点连接下一组的头节点
- head.next = reverseKGroup(tail,k);//此处的tail就是每一组的头结点
- return pre;
-
-
- }
复杂度分析:
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

思路:
- public ListNode Merge(ListNode list1,ListNode list2) {
- if (list1 == null && list2 == null) {
- return null;
- }
- //建一个新的链表,将合并后的放入其中
- ListNode newHead = new ListNode(-1);
- ListNode tmp = newHead;
- while (list1 != null && list2 != null) {
- if (list1.val < list2.val) {
- //放入一个节点
- tmp.next = list1;
- list1 = list1.next;
- tmp = tmp.next;
- }else {
- tmp.next = list2;
- list2 = list2.next;
- tmp = tmp.next;
- }
- }
- //到这走完其中一个链表了
- if(list1 != null) {
- tmp.next = list1;//因为是有序了,所以连接好前面一个节点就写
- }
- if (list2 != null) {
- tmp.next = list2;
- }
-
- return newHead.next;
- }
- }
时间复杂度:O(m+n)
空间复杂度:O(m+n),每一次递归,递归栈都会保存一个变量,最差情况会保存(m+n)个变量
判断给定的链表中是否有环。如果有环则返回true,否则返回false。

思路
- public boolean hasCycle(ListNode head) {
- if(head == null) return false;
- ListNode fast = head;
- ListNode slow = head;
- while(fast != null && fast.next != null) {
- fast = fast.next.next;
- slow = slow.next;
- if(fast == slow) {
- return true;
- }
- }
- return false;
-
- }
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

思路;
- public ListNode hasCycle(ListNode head) {
- if(head == null) return null;
- ListNode fast = head;
- ListNode slow = head;
- while(fast != null && fast.next != null) {
- fast = fast.next.next;
- slow = slow.next;
- if(fast == slow) {
- return slow;
- }
- }
- return null;
- }
- public ListNode EntryNodeOfLoop(ListNode pHead) {
- ListNode slow = hasCycle(pHead);
- //没有环
- if(slow == null) return null;
- //快指针回到表头
- ListNode fast = pHead;
- //再次相遇就是入口
- while(fast != slow){
- fast = fast.next;
- slow = slow.next;
- }
- return slow;
- }
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
思路:
- public ListNode FindKthToTail (ListNode pHead, int k) {
- ListNode fast = pHead;
- ListNode slow = pHead;
- //快指针先行k步
- for (int i = 0; i < k; i++) {
- if (fast != null) {
- fast = fast.next;
- } else {
- //达不到k步说明链表过短,没有倒数k
- return null;
- }
-
- }
- //快慢指针同步,快指针先到底,慢指针指向倒数第k个
- while(fast != null) {
- fast = fast.next;
- slow = slow.next;
- }
- return slow;
- }
给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
思路:
- public ListNode removeNthFromEnd (ListNode head, int n) {
- // write code here
- ListNode fast = head;
- ListNode slow = head;
-
- ListNode pre = new ListNode(-1);
- pre.next = head;
- ListNode cur = pre;
-
- while(n != 0) {
- fast = fast.next;
- n--;
- }
-
- while(fast != null) {
- fast = fast.next;
- cur = slow;
- slow = slow.next;
- }
-
- cur.next = slow.next;
- return pre.next;
- }

思路:
1. 利用双指针法,先算出两个链表的长度
2.然后走两个链表的长度差值
在一起走,相遇就是一第一个公共节点
- public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
- if (pHead1 == null && pHead2 == null) {
- return null;
- }
- ListNode pl = pHead1;
- ListNode ps = pHead2;
- int len1 = 0;
- int len2 = 0;
- //1.遍历两个链表,求长度
- while(pl != null) {
- len1++;
- pl = pl.next;
- }
- //走完再回到头节点
- pl = pHead1;
-
- while (ps != null) {
- len2++;
- ps = ps.next;
- }
- ps = pHead2;
-
- //差值
- int len = len1 - len2;
- //pl始终是记录最长链表的
- //len小于0 ,说明pHead2长
- if (len < 0) {
- pl = pHead2;
- ps = pHead1;
- len = len2 - len1;
- }
- //标记好最长的之后,长的开始走差值
- while (len > 0) {
- pl = pl.next;
- len--;
- }
-
- //到这里,剩下的节点数相同,
- //同时走,相遇就是第一个公共节点
- while (ps != pl) {
- ps = ps.next;
- pl = pl.next;
- }
- return ps;
-
- }
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。


思路:
- public ListNode addInList (ListNode head1, ListNode head2) {
- // write code here
- //任意链表为空,返回另一个
- if (head1 == null) return head2;
- if (head2 == null) return head1;
- //反转两个链表
- head1 = ReverseList(head1);
- head2 = ReverseList(head2);
- //添加表头
- ListNode res = new ListNode(-1);
- ListNode head = res;
- //进位符号
- int carry = 0;
- //只要某个链表还有或者进位还有
- while (head1 != null || head2 != null || carry != 0) {
- //链表不为空则取其值
-
- int val1 = head1 == null ? 0 : head1.val;
- int val2 = head2 == null ? 0 : head2.val;
- //相加
- int temp = val1 + val2 + carry;
- //获取进位
- carry = temp / 10;
- temp %= 10;
- //添加元素
- head.next = new ListNode(temp);
- head = head.next;
- //移动下一个
- if (head1 != null) head1 = head1.next;
- if (head2 != null) head2 = head2.next;
- }
- //结果反转回来
- return ReverseList(res.next);
-
- }
-
- public ListNode ReverseList(ListNode pHead) {
- if (pHead == null) return null;
- ListNode cur = pHead;
- ListNode pre = null;
- while (cur != null) {
- ListNode curNext = cur.next;
- cur.next = pre;
- pre = cur;
- cur = curNext;
- }
- return pre;
- }
思路:
1.找中间节点
2.反转后半链表
3.两边往中间走,判断
- public boolean isPail (ListNode head) {
- // 1.找中间节点
- ListNode fast = head;
- ListNode slow = head;
- while(fast != null && fast.next != null) {
- fast = fast.next.next;
- slow = slow.next;
- }
- //2.反转后半链表
- ListNode pre = null;
- while(slow != null) {
- ListNode node = slow.next;
- slow.next = pre;
- pre = slow;
- slow = node;
- }
- //两边往中间走,判断
- ListNode cur = head;
- while(pre != null) {
- if(cur.val != pre.val) {
- return false;
- }
- cur = cur.next;
- pre = pre.next;
- }
- return true;
- }
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。

思路:
- public ListNode oddEvenList (ListNode head) {
- // write code here
- //如果链表为空,不用重排
- if (head == null) return head;
- // odd 开头指向第一个节点
- ListNode odd = head;
- //even 指向开头第二个
- ListNode even = head.next;
- //指向even开头
- ListNode evenHead = even;
- while (even != null && even.next != null) {
- //odd连接even的后一个,即奇数位
- odd.next = even.next;
- //odd进入后一个奇数位
- odd = odd.next;
- //even连接后一个奇数的后一位,即偶数位
- even.next = odd.next;
- even = even.next;
-
- }
- //even整体接在odd后面
- odd.next = evenHead;
- return head;
- }
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
思路:
-
- public ListNode deleteDuplicates (ListNode head) {
- // write code here
- if (head == null) return null;
- ListNode cur = head;
- while (cur != null && cur.next != null) {
- if (cur.val == cur.next.val) {
- cur.next = cur.next.next;
- }else {
- cur = cur.next;
- }
-
- }
- return head;
- }
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
思路:
- public ListNode deleteDuplicates (ListNode head) {
- // write code here
- if(head == null) return null;
- ListNode pre = new ListNode(-1);
- pre.next = head;
- ListNode cur = pre;
- while(cur.next != null && cur.next.next != null) {
- if(cur.next.val == cur.next.next.val) {
- int temp = cur.next.val;
- while(cur.next != null && cur.next.val == temp) {
- cur.next = cur.next.next;
- }
- } else {
- cur = cur.next;
- }
- }
- return pre.next;
- }