- 移除链表元素
/**
* 删除链表中等于给定值 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个结点
*
* 输入一个链表,输出该链表中倒数第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
基本思路:遍历链表中的每个结点,和value值比对,同则删不同则将结点尾插到新链表中
- 反转单链表
基本思想:遍历每个结点,头插到result链表
- 合并两个有序链表
基本思路:遍历两个链表,比对两个链表每个结点的val,小的优先尾插到新链表中,当一个表被遍历空后,将另一个表剩余结点直接拼到新链表后
- 利用x把链表分割成<x和>=x两部分
基本思路:遍历链表的每个结点,若<x,尾插到small表,否则,尾插到big表中,最后将small和big拼接起来(注意判空)
- 双引用遍历–求中间结点
基本思路:快慢引用,快2慢1.在一个周期内,快的走两步慢的走一步(注意快的每走一步都要判空),快的走到尽头慢的即为所求
- 双引用遍历–求倒数第k个结点
基本思路:前后引用,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