链表类编程汇总

链表是补充数组数据结构的另一种常见数据结构。与数组类似,它也是线性数据结构,以线性方式存储元素。

然而,与数组不同的是,它不会将元素存储在连续的位置;相反,它会将其分散存储在内存中,彼此通过节点相互连接。链表是节点列表,其中每个节点包含存储的值和下一个节点的地址。

由于这种结构,在链表中添加或删除元素变得很简单,因为你只需要改变链接而不是创建数组,但是这样会使搜索变得困难,并且经常需要 O(n) 的时间复杂度才能在单个链表中找到某个元素。

这篇文章(https://javarevisited.blogspot.com/2013/07/difference-between-array-and-linked-list-java.html)提供了更多关于数组和链表数据结构之间差异的信息。

链表还有多种变体,如单链表,即允许在一个方向(正向或反向)上遍历;双链表则允许你在两个方向(向前或向后)遍历;最后是循环链表,它形成一个循环。

要解决关于链表的问题,掌握递归知识很重要,因为链表是递归数据结构。

如果你从链表中取出一个节点,剩下的数据结构仍然是链表,因此,许多链表问题的递归解比迭代解更简单。

1.找到链表的中间元素
思路一:快慢指针
快指针每次走两格,慢指针走一格。

	public static  void findMid(ListNode pHead) {
		ListNode pfast=pHead;
		ListNode pslow=pHead;
		while(pfast.next!=null) {
			if(pfast.next.next!=null) {
				pfast=pfast.next.next;
				pslow=pslow.next;
			}else {
				pfast=pfast.next;
			}
		}
		System.out.println(pslow.val);
	}

2.找链表中的环
https://www.cnblogs.com/xudong-bupt/p/3667729.html
<1>是否存在环

public class Solution {
    public boolean hasCycle(ListNode head) {
		if (head == null || head.next == null)
			return false;
		ListNode fast = head;
		ListNode slow = head;
		while (fast != null && fast.next != null) {
			fast = fast.next.next;
			slow = slow.next;
			if (slow == fast)
				return true;
		}
		return false;
    }
}

<2>找环的入口

public class Solution {
    public ListNode detectCycle(ListNode head) {
       ListNode fast=head;
		ListNode slow=head;
		while(fast!=null&&fast.next!=null) {
			slow=slow.next;
			fast=fast.next.next;
			if(slow==fast)
				break;
		}
		if(fast==null||fast.next==null) {
			return null;
		}
		slow=head;
		while(slow!=fast) {
			slow=slow.next;
			fast=fast.next;
		}
		return fast;
    }
}

3.反转链表
迭代法:每次迭代建next节点存储当前节点的下个节点,防止断链。

	public ListNode reverseList(ListNode head) {
		ListNode pre = null;
		ListNode now = head;
		while (now != null) {
			ListNode next = now.next;
			now.next = pre;
			pre = now;
			now = next;
		}
		return pre;
	}

递归法

public Node reverse2(Node node, Node prev) {
    if (node.next == null) {
      node.next = prev;
      return node;
    } else {
      Node re = reverse2(node.next, node);
      node.next = prev;
      return re;
    }
}

4.删除排序链表中的重复节点
<1> 只删除重复的 1-2-2 =>1-2

		ListNode curr=head;
		while(curr!=null&&curr.next!=null) {
			if(curr.val==curr.next.val) {
				curr.next=curr.next.next;
				}else {
					curr=curr.next;
				}
		}
		return head;
	}

<2>重复的全部删除 1-2-2 =>1

public static ListNode deleteDuplicates(ListNode head) {
	 ListNode ne=new ListNode(-1);
	 ListNode tmp=ne;
	 while(head!=null&&head.next!=null) {		 
		 if(head.val==head.next.val) {
			 while(head.next!=null&&head.val==head.next.val) {
				 head=head.next;
			 }
			 head=head.next;
		 }else {
			 tmp.next=head;
			 tmp=tmp.next;
			 head=head.next;
		 }
	 }
	 tmp.next=head;
	 return ne.next;	        
   }

5.反转部分链表

  1. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
思路:
先找到要反转的节点的前驱节点,然后实现反转其后K个节点的方法。
reverse(ListNode front, int k)反转front(不包括from)其后的k个节点
head 保存第一个反转的节点,from保存最后一个反转的节点。

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode res = new ListNode(0);
		res.next = head;
		ListNode re = res;
		int count = 1;
		while (m > count) {
			re = re.next;
			count++;
		}
		reverse(re, n - m+1);
		return res.next;
	}

	private static ListNode reverse(ListNode front, int k) {
		ListNode from = front.next;
		if (from == null)
			return front;
		ListNode head = from;
		ListNode cur = from.next;
		ListNode tmp = null;
		while (k > 1 && cur != null) {
			tmp = cur.next;
			cur.next = from;
			from = cur;
			cur = tmp;
			k--;
		}
		head.next = cur;
		front.next = from;
		return head;
	}
}

6.删除节点

public static void deleteNode(ListNode head, ListNode node) {
		ListNode cur = head;
		ListNode pre = null;
		while (cur != null) {
			ListNode next = cur.next;
			if (cur.val == node.val) {
				pre.next = next;
			}
			pre = cur;
			cur = next;
		}
	}

7.删除倒数第k个节点
思路:快慢指针。快指针先快走n步,慢指针与指针同时走,快指针到尾时,慢指针指向需要删除的元素。
trike:先设一个哑结点用于解决极端情况。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dumy=new ListNode(0);
		dumy.next=head;
		ListNode pre = dumy;
		while (n > 0) {
			pre = pre.next;
			n--;
		}
		ListNode cur=dumy;
		ListNode tmp=null;
		while(pre!=null) {
			tmp=cur;
			pre=pre.next;
			cur=cur.next;
		}
		tmp.next=cur.next;
		return dumy.next;
    }
}

8.删除乱序节点中的重复节点
思路:遍历两遍
注意内循环节点需要先行,才能定位到重复的第一个节点。

static void  remove_duplicates(ListNode head) {
		ListNode dumy=new ListNode(0);
		dumy.next=head;
		ListNode first=dumy;
		ListNode second=dumy;
		while(first!=null&&first.next!=null) {
			second=first;
			while(second.next!=null) {
				if(first.val==second.next.val) {
					second.next=second.next.next;
				}else
				second=second.next;
			} 
			first=first.next;
		}	
	}

利用hashSet

static void  remove_duplicates(ListNode head) {
		ListNode dumy=new ListNode(0);
		dumy.next=head;
		ListNode cur=dumy;
		ListNode pre=dumy;
		HashSet<Integer> hs = new HashSet<>();
		while(cur!=null) {
			int curval=cur.val;
			if(hs.contains(curval)) {
				pre.next=cur.next;
			}else {
				hs.add(curval);
				pre=cur;
			}
			cur=cur.next;
		}
	}

两数相加
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

从低位到高位处理

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode res = new ListNode(-1);
	ListNode cur = res;
	int carry = 0;
	while(l1!=null||l2!=null) {
		int d1 = l1==null ? 0 : l1.val;
		int d2 = l2==null ? 0 : l2.val;
		int sum = d1+d2+carry;
		carry = sum>10?1:0;
		cur.next=new ListNode(sum%10);
		cur = cur.next;
		if(l1!=null) l1=l1.next;
		if(l2!=null) l2=l2.next;
		
	}
	if(carry==1) cur.next=new ListNode(1);
	return res.next;
    }
}

合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
合并排序数组类似
问题:如果一个链表为空,则结果链表的next指向另一个非空链表即可,不用再迭代。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        		ListNode res = new ListNode(-1);
		ListNode cur = res;
		while (l1 != null && l2 != null) {
			// cur.next = l1.val>=l2.val ? l2:l1;
			if (l1.val >= l2.val) {
				cur.next = l2;
				l2 = l2.next;
			} else {
				cur.next = l1;
				l1 = l1.next;
			}
			cur = cur.next;
		}
        cur.next = l1==null?l2:l1;
		return res.next;
    }
}

链表排序
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
输入: 4->2->1->3
输出: 1->2->3->4
思路:归并排序;
利用快慢指针找到mid节点;
然后断链;

public ListNode sortList(ListNode head) {
	if(head==null||head.next==null)
		return head;
	ListNode fast=head;
	ListNode slow=head;
	while(fast.next!=null&&fast.next.next!=null) {
		fast=fast.next.next;
		slow=slow.next;
	}
	ListNode mid = slow.next;
	slow.next=null;
	ListNode l1 = sortList(head);
	ListNode l2 = sortList(mid);
	ListNode sorted = merge(l1, l2);
	return sorted;
	
    
}
private ListNode merge(ListNode l1,ListNode l2){

        if(l1 == null){
            return l2;
        }
        if(l2 == null){
            return l1;
        }

        ListNode head,tmp;
        if(l1.val < l2.val){
            head = l1;
            l1 = l1.next;
        }else{
            head = l2;
            l2 = l2.next;
        }

        tmp = head;

        while(l1!=null && l2!=null){
            if(l1.val < l2.val){
                tmp.next = l1;
                tmp = tmp.next;
                l1 = l1.next;
            }else{
                tmp.next = l2;
                tmp = tmp.next;
                l2 = l2.next;
            }
        }

        if(l1 == null){
            tmp.next = l2;
        }
        if(l2 == null){
            tmp.next = l1;
        }
        return head;
    }

猜你喜欢

转载自blog.csdn.net/sky_noodle/article/details/82913043