/** * 删除链表中等于给定值 val 的所有节点。 * * 示例: * * 输入: 1->2->6->3->4->5->6, val = 6 * 输出: 1->2->3->4->5 * * 基本思路:建立新链表,遍历筛选非val值尾插进新链表中 */ public class RemoveEle { public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode removeElements(ListNode head, int val) { ListNode result = null; //创建结果链表,初始为null ListNode last = null; //结果链表的尾结点,初始为null ListNode cur = head; while(cur.next != null){ ListNode next = cur.next; //此处为cur存档 if(cur.val != 6){ //尾插 if(result == null){ result = cur; }else{ last.next = cur; } last = cur; //更新链表的最后一个元素 } cur = next; //回档,继续读原链表的下一个元素 } return result; } }
/** * 反转一个单链表。 * * 示例: * * 输入: 1->2->3->4->5->NULL * 输出: 5->4->3->2->1->NULL * * 基本思路:创建新链表,将原表的元素头插进新表中 */ public class ReverseListSolution { public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode reverseList1(ListNode head) { ListNode result = null; ListNode cur = head; while(cur != null){ ListNode next = cur.next; //存档 //头插 cur.next = result; result = cur; cur = next; //回档 } return result; } /** * 进阶: * 三引用遍历反转链表 * * 基本思路:定义三个结点: * prev--指向前一个结点, * cur--指向当前结点, * next--指向下一结点 * 当cur == null时停止遍历 */ public ListNode reverseList2(ListNode head) { if(head == null){ return null; } ListNode prev = null; ListNode cur = head; //prev-->cur-->next while(cur != null){ ListNode next = cur.next; cur.next = prev; //反转 next-->cur-->prev prev = cur; cur = next; } // 返回反转后的链表 此时prev已经指向最后一个结点,cur和next都指向空 return prev; } }
/** * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 * * 示例: * * 输入:1->2->4, 1->3->4 * 输出:1->1->2->3->4->4 * * 基本思路:两个有序表合并 * 判空 * cur1和cur2同时不为空:尾插 * cur1和cur2有一个为空(在合并过程中,一个表的元素全被转移到新表中),将另一个表直接尾插进新表 */ public class MergeTwoLists { public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if(l1 == null){ return l2; } if(l2 == null){ return l1; } ListNode cur1 = l1; ListNode cur2 = l2; ListNode result = null; //创建新链表,初始为空 ListNode last = null; //创建最后一个结点,初始为null while(cur1 != null && cur2 != null){ if(cur1.val <= cur2.val){ ListNode next = cur1.next; //存档 //cur1尾插 if(result == null){ result = cur1; }else{ last.next = cur1; } last = cur1; //更新最后一个结点 cur1 = next; //回档 }else{ ListNode next = cur2.next; //cur2尾插 if(result == null){ result = cur2; }else{ last.next = cur2; } last = cur2; //更新最后一个结点 cur2 = next; //回档 } } //当cur1或cur2两个表在合并过程中,一个表的元素全被转移到新表中,剩下的另一个表直接尾插 if(cur1 == null){ last.next = cur2; } if(cur2 == null){ last.next = cur1; } return result; } }
/** * 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 * * 给定一个链表的头指针 ListNode pHead,请返回重新排列后的链表的头指针。注意:分割以后保持原来的数据顺序不变。 * * 基本思路:创建新的链表,以x为界限,小于x的结点尾插存放在small,大于或等于x的结点尾插存放在big,最终合并两个表 */ public class Partition { public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode partition(ListNode pHead, int x) { ListNode small = null; //小于x的存放链表 ListNode smallLast = null; //小于x的链表的最后一个结点 ListNode big = null; //大于或等于x的存放链表 ListNode bigLast = null; //大于或等于x的链表的最后一个结点 ListNode cur = pHead; //定义链表元素变量,初始为pHead while(cur != null){ ListNode next = cur.next; //存档 //小于x,尾插至small if(cur.val < x){ if(small == null){ small = cur; } else { smallLast.next = cur; } smallLast = cur; //更新最后一个结点 } else { //大于或等于x,尾插至big if(big == null){ big = cur; }else{ bigLast.next = cur; } bigLast = cur; //更新最后一个结点 bigLast.next =null; } cur = next; //回档 } //当一个表为空 if(small == null){ return big; } else { smallLast.next = big; return small; } } }
/** * 链表的中间结点 * * 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 * * 如果有两个中间结点,则返回第二个中间结点。 * * 示例 1: * * 输入:[1,2,3,4,5] * 输出:此列表中的结点 3 * 返回的结点值为 3 。 * * 示例 2: * * 输入:[1,2,3,4,5,6] * 输出:此列表中的结点 4 * * 基本思路:双指针遍历---->快慢指针:快2慢1 * 返回慢指针 */ public class MiddleNode { public class ListNode { int val; ListNode next; ListNode(int x) { val = x; } } public ListNode middleNode(ListNode head) { ListNode fast = head; ListNode slow = head; //快2慢1 while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; } /* while(fast != null){ fast = fast.next; if(fast == null){ break; } slow = slow.next; fast = fast.next; } */ return slow; } }
/** * 链表中倒数第k个结点 * * 输入一个链表,输出该链表中倒数第k个结点。 * * 基本思路:定义两个结点变量(front,back) 前后引用 * 让front先走k步,back再开始走 * 当front为空的时候代表走到了链表的尽头,此时的back所指向的就是倒数第k个数 * * 判断两个状况:若front为空并且k大于结点数时,意味着找不到这个元素,直接返回null [1,6) i=6,k=7 * 当front为null,而i>=k时,直接返回head [1,6] i=6,k=6 * */ public class FindKthToTail { public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode FindLastKth(ListNode head,int k) { ListNode front = head; ListNode back = head; int i; for (i = 0; front != null && i < k; i++) { front = front.next; //front先走到正数第k个元素 } if (front == null && i < k) { // k 大于 结点个数 return null; } else if (front == null) { //[1,6] return head; } //front和back一前一后对链表进行遍历,直到front == null while (front != null) { front = front.next; back = back.next; } return back; } }
/** * 链表的回文结构 * * 对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。 * * 给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。 * * 测试样例: * 1->2->2->1 * 返回: * true * * 基本思路:找中间结点+反转后半部分链表与前半部分进行比较 * * 先求出链表的长度-->求链表的一半长度---->找中间结点 * 再反转后半部分链表 * 最后判断回文 */ public class PalindromeList { public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } //获取链表长度 public int getLength(ListNode head){ int len = 0; ListNode cur = head; while(cur != null){ len ++; cur = cur.next; } return len; } //反转函数 public ListNode reverse(ListNode head){ ListNode result = null; ListNode cur = head; while(cur != null){ ListNode next = cur.next; //头插 cur.next = result; result = cur; cur = next; } return result; } //判断回文 public boolean chkPalindrome(ListNode A) { ListNode middle = A; int len = getLength(A); int halfLen = len/2; //找到中间结点 for(int i = 0; i < halfLen; i++){ middle = middle.next; } //从中间结点middle开始到尾结点进行链表反转 ListNode r = reverse(middle); ListNode c1 = A; //c1初始化为原链表头结点 ListNode c2 = r; //c2初始化为反转后的链表头结点 //遍历进行值比对 while(c1 != null && c2 != null){ if(c1.val != c2 .val){ return false; } else { c1 = c1.next; c2 = c2.next; } } return true; } }
/** * 删除链表中重复的结点 * * 在一个有序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 * * 例如,链表1->2->3->3->4->4->5 * 处理后为 1->2->5 * * 基本思路: * 1.创建结点,假结点dummy,指针结点prev,p1,p2 * 2.令dummy.next->head prev = dummy p1->head p2->head.next * 3.在p2不为空的条件下对p1,p2的值进行比较 * 3.1 当P1和P2值不等,指针统一后移 * 3.2 当P1和P2值相等且P2不为空,删除当前结点,仅将P2后移一位 */ public class DeleteDuplication { public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode deleteDuplication(ListNode pHead) { if(pHead == null){ return null; } ListNode dummy = new ListNode(0); //为前驱结点变量赋值,消除第一个结点没有前驱的特殊性 dummy.next = pHead; ListNode prev = dummy; // prev 永远是 p1 的前驱结点,用来删除结点 ListNode p1 = pHead; ListNode p2 = pHead.next; //比较p1和p2的值 while (p2 != null) { if (p1.val != p2.val) { //值不同结点变量统一后移 prev = prev.next; p1 = p1.next; p2 = p2.next; } else { //值相同p2后移,其余两个结点不动 while (p2 != null && p1.val == p2.val) { p2 = p2.next; } prev.next = p2; //将两个相同的p1和p2所指的结点同时删除 p1 = p2; //p1后移 if (p2 != null) { p2 = p2.next; //p2后移 } } } return dummy.next; } }
小结:
基本思路:遍历链表中的每个结点,和value值比对,同则删不同则将结点尾插到新链表中
基本思想:遍历每个结点,头插到result链表
基本思路:遍历两个链表,比对两个链表每个结点的val,小的优先尾插到新链表中,当一个表被遍历空后,将另一个表剩余结点直接拼到新链表后
基本思路:遍历链表的每个结点,若<x,尾插到small表,否则,尾插到big表中,最后将small和big拼接起来(注意判空)
基本思路:快慢引用,快2慢1.在一个周期内,快的走两步慢的走一步(注意快的每走一步都要判空),快的走到尽头慢的即为所求
基本思路:前后引用,front先走k步back再走,这样保证走到最后front走到链表尽头,此时的back就是倒数第k个数(注意K与链表长度的对比)
基本思路:先找中间结点,然后反转后半部分链表,最后将后半部分与前半部链表的val进行遍历值比对
基本思路:创建假结点dummy,前驱结点prev,值对比前后两个结点p1,p2。当p2不为空且p1,p2值不相等时,全部结点指针变量后移一位,相等则让p2后移一位,prev删除相同结点,集体后移一位。
完整代码:https://github.com/Loinbo/Data-Structure/tree/master/LinkedListTest/src/com/lamb