5个常见的链表操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012292754/article/details/83154118

1 单链表翻转

1.1 迭代版本

时间复杂度 O(n),空间复杂度 O(1)

ListNode ReverseList(ListNode head)
{
	ListNode temp = null, nextNode = null;

	while(head != null) {
		nextNode = head;
		head.setNext(temp);
		temp = head;
		head = nextNode;
	}
	return temp;
}

1.2

public static Node reverse(Node list){
	Node headNode = null;
	Node preNode = null;
	Node currNode = list;

	while( currNode != null) {
		Node nextNode = currNode.next;
		if(nextNode == null) {
			headNode = currNode;
		}

		currNode.next = preNode;
		preNode = currNode;
		currNode = nextNode;

	}

	return headNode;
}

2 链表中环的检测

Floyd 环判定法,使用在链表中具有不同移动速度的指针,一旦它们进入环就会相遇,即表示存在环。

boolean DoesListContainsLoop(ListNode head){
	//时间复杂度O(n),空间复杂度O(1)
	if(head==null) return fasle;
	ListNode slowPtr=fastPtr=head;
	while(fastPtr.getNext()!=null && fastPtr.getNext().getNext()!=null) {
		slowPtr=slowPtr.getNext();
		fastPtr=fastPtr.getNext().getNext();
		if(slowPtr==fastPtr)
			return true;

	}
	return false;
}

public static boolean checkCircle(Node list){
	if(list == null) return false;
	Node fast = list.next;
	Node slow = list;

	while(fast != null && fast.next != null) {
		fast = fast.next.next;
		slow = slow.next;

		if(slow == fast) return true;
	}

	return fasle;
}

2.1 判断给定的链表是否存在环,如果存在,找到环的起始点

思路分析:在找到环后,初始化 slowPtr 使其指向表头节点。然后slowPtr 和 fastPtr 从各自的位置开始移动,每次只移动一个节点,它们相遇的位置就是环的起始位置。(可以用这种方法删除环)

ListNode FindBerginofLoop(ListNode head){
// 时间复杂度 O(n),空间复杂度 O(1)

	if(head==null)
		return null;
	ListNode slowPtr=fastPtr=head;
	boolean loopExists=false;
	while(fastPtr.getNext()!=null && fastPtr.getNext().getNext()!=null) {
		slowPtr = slowPtr.getNext();
		fastPtr = fastPtr.getNext().getNext();
		if(slowPtr==fastPtr) {
			loopExists = true;
			break;
		}
	}

	if(loopExists) {
		slowPtr=head;
		while(slowPtr!=fastPtr) {
			slowPtr = slowPtr.getNext();
			fastPtr = fastPtr.getNext();
		}
		return slowPtr; //返回环的开始节点
	}

	return null; //环不存在

}

2.2 在Floyd 环判定算法中,如果两个指针每次分别移动2个节点和3个节点,而不是移动1个节点和2个节点,算法任然有效吗?

有效,但是复杂度可能增加。

2.3 判定给定的链表中是否存在环,若存在,返回环的长度

在找到链表中存在环后,保持slowPtr的指针不变,fastPtr 指针继续移动。每次移动 fastPtr 指针时,计数器变量加 1 ,直到 再一次回到 slowPtr 指针所在的位置。

// 时间复杂度 O(n),空间复杂度 O(1)
int findLoopLength(ListNode head){
	ListNode slowPtr = fastPtr = heaed;
	boolean loopExists = fasle;
	int count = 0;
	if(head == null) return 0;

	while(fastPtr.getNext() != null && fastPtr.getNext().getNext() != null) {
		slowPtr = slowPtr.getNext();
		fastPtr = fastPtr.getNext().getNext();
		if(slowPtr == fastPtr) {
			loopExists = true;
			break;
		}
	}

	if(loopExists) {
		fastPtr = fastPtr.getNext();
		count++;
		while(slowPtr != fastPtr) {
			fastPtr = fastPtr.getNext();
			count++;
		}
		count++;
		return count;
	}

	return 0;
}

3 两个有序链表的合并

3.1 方法1

// 第一个 while 循环,将l1 和 l2 进行比较,谁小就合并到 listNode,直到l1 或者 l2 为空
//  第二、三个 while 循环 将了l1 或者l2 剩下的节点合并到 listNode

ListNode mergeSortedList(ListNode l1,ListNode l2){
	ListNode listNode = new ListNode(0);
	ListNode head = listNode;

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

	while(l1 != null) {
		listNode.next = l1;
		l1 = l1.next;
		listNode = listNode.next;
	}

	while(l2 != null) {
		listNode.next = l2;
		l2 = l2.next;
		listNode = listNode.next;
	}

	return head.next;
}

3.2 方法2 递归法

//分治思想,每次拿一个小的出来,每次的动作相同
ListNode MergeLists(ListNode a,ListNode b){
	ListNode result = null;
	if(a==null) return b;
	if(b==null) return a;

	if(a.getData() <= b.getData()) {
		result = a;
		result.setNext(MergeLists(a.getNext(),b));
	}else{
		result = b;
		result.setNext(MergeLists(b.getNext(),a));
	}

	return result;
}

3.3

public static Node mergeSortedLists(Node la,Node lb){
	if(la == null) return lb;
	if(lb == null) return la;

	Node p = la;
	Node q = lb;
	Node head;

	if(p.data < q.data) {
		head = p;
		p = p.next;
	}else{
		head = q;
		q = q.next;
	}
	Node r = head;

	while(p != null && q!= null) {
		if(p.data < q.data) {
			r.next = p;
			p = p.next;
		}else{
			r.next = q;
			q = q.next;
		}
		r = r.next;
	}

	if(p != null) {
		r.next = p;
	}else{
		r.next = q;
	}

	return head;

}

4 删除链表倒数第 n 个节点

public static Node deleteLastKth(Node list,int k){
	Node fast = list;
	int i = 1;
	while( fast != null && i < k) {
		fast = fast.next;
		++i;
	}

	if(fast == null) return list;

	Node slow = list;
	Node pre = null;
	while(fast.next != null) {
		fast = fast.next;
		pre = slow;
		slow = slow.next;
	}

	if(pre == null) {
		list = list.next;
	}else{
		pre.next = pre.next.next;
	}

	return list;

}

5 求链表的中间节点

5.1 蛮力法

在链表中对每个节点统计其后的节点的个数,然后判定其是否为中间节点。
时间复杂度 O(N^2),空间复杂度 O(1)

5.2 一次扫描搞定

时间复杂度 O(n),空间复杂度 O(1)
让第一个指针的移动速度是另一个的2倍,当第一个到达表尾的时候,另一个指针则指向中间节点。

ListNode findMiddle(ListNode head){
	ListNode slowPtr = fastPtr = head;
	//不断循环,直到达到表尾(next的后继指针为 null,表示达到最后一个节点)
	int i = 0;
	while(fastPtr.getNext() != null) {
		if(i == 0) {
			fastPtr = fastPtr.getNext();
			i = 1;
		}
		if(i == 1) {
			fastPtr = fastPtr.getNext();
			slowPtr = slowPtr.getNext();
			i = 0;
		}

	}

	return slowPtr;
}

public static Node findMidNode(Node list){
	if(list == null) return null;
	Node first = list;
	Node slow = list;

	while(fast.next != null && fast.next.next != null) {
		fast = fast.next.next;
		slow = slow.next;
	}

	return slow;

}

猜你喜欢

转载自blog.csdn.net/u012292754/article/details/83154118