左神算法笔记(六)——链表

回文链表

  1. 创建一个栈,将链表中所有数据存入栈中,再将栈中数据与链表中的所有数据进行比较。
  2. 利用单步指针和双步指针,当双步指针最后到链表尾的时候,将后半部分链表指向取反,再从链表头和链表尾同时开始对比数据大小,实现回文链表的判断。空间复杂度为O(1)。
public static class Node{
	public int value ;
	public Node next;
	public Node(int data){
		this.value = data;
	}
}

//需要n个额外空间
public static boolean isPalinddrome1(Node head){
	Stack<Node> stack = new Stack<Node>();//建立一个node类型的栈
	Node cur = head;
	while(cur!=null){
		stack.push(cur);
		cur = cur.next;
	}
	while(head!= null){
		if(head.value != stack.pop().value){
			return false
		}
		head = head.next;
	}
	return true;
}


//需要n/2个额外空间
public static boolean isPalindrome2(Node node){
	//在只包含0个或1个数的时候,必定满足回文结构
	if(head == null ||head.next == null){
		return true;
	}
	//设置right为单步遍历,cur为双步遍历,因此需要提前将只包含0,1个数据的链表剔除,不然无法进行判断。
	Node right = head.next;
	Node cur = head;
	while(cur.next!=null &&cur.next.next !=null){
		right = right.next;
		cur = cur.next.next;
	}
	Stack<Node> stack = new Stack<Node>();
	while(right != null){
		stack.push(right);
		right = right.next;
	}
	while(!stack.isEmpty()){
		if(head.value != stack.pop().value){
			return false;
		}
		head = head.next;
	}
	return ture;
}

//需要O(1)额外空间
public static boolean isPalindrome3(Node head){
	if(head == null|| head.next == null){
		return ture;
	}
	Node n1 = head;
	Node n2 = head;
	while(n2.next !=null&&n2.next.next != null){
		n1 = n1.next;
		n2 = n2.next.next;
	}
	//n2变成了右半部分第一个点,将左右两边分开
	n2 = n1.next;
	n1.next = null;
	Node n3 = null;
	//通过while循环将右半部分的链表指向取反
	while(n2! = null){
		n3 = n2.next;
		n2.next = n1;
		n1 = n2;
		n2 = n3;
	}
	n3 = n1;//n3表示的是最后的一个节点
	n2 = head;//n2为左边第一个节点
	boolean res = true;
	while(n1 != null && n2 != null){
		if(n1.value != n2.value){
			return false;
		}
		n1 = n1.next;
		n2 = n2.next;
	}
	//最后再将整个链表复原
	n1 = n3.next;
	n3.next = null;
	while(n1 != null){
		n2 = n1.next;
		n1.next = n3;
		n3 = n1;
		n1 = n2;
	}
	return res;
}
	

单向链表按照某值划分成左边小,中间相等,右边大的形式

  1. 荷兰国旗问题,额外准备一个数组。荷兰国旗问题不具有稳定性。
  2. 设置三个节点,分别为小于mem,等于mem,大于mem。每个节点设置两个变量,分别为head和end,每次增加数值则将end++,数据存放入该链表之中。
public static class Node{
	public int value;
	public Node next;
	public Node(int data){
		this.value = data;
	}
}

public static Node listPartition1(Node head,int pivot){
	if(head == null){
		return head;
	}
	Node cur = head;
	int i = 0;
	//负责将整个链表长度计算出来,从而可以知道新建多长的数组
	while(cur !=null){
		i++;
		cur = cur.next;
	}
	//新建数组
	Node[] nodeArr = new Node[i];
	i = 0;
	cur = head;
	//将链表中的数据放入数组之中
	for(i = 0;i!= nodeArr.length;i++){
		nodeArr[i] = cur;
		cur = cur.next;
	}
	//利用荷兰国旗问题进行排序,荷兰国旗在之前讲解快速排序的时候讲到了,不了解可以前翻。
	arrPartition(nodeArr,pivot);
	//将数组改成链表的形式返回
	for(i = 1;i != nodeArr.length;i++){
		nodeArr[i-1].next =nodeArr[i];
	}
	nodeArr[i-1].next = null;
	return nodeArr[0];
}
//实现了将整个Node数组中的数据进行了交换
public static void arrpartition(Node[] nodeArr,int pivot){
	int small = -1;
	int big = nodeArr.length;
	int index = 0;
	while(index != big){
		if(nodeArr[index].value<pivot){
			swap(nodeArr,++small,index++);
		}else if(nodeArr[index].value>pivot){
			swap(nodeArr,--big,index);
		}else{
			index++;
		}
	}
}
public static void swap(Node[] nodeArr,int a,int b){
	Node tmp = nodeArr[a];
	nodeArr[a] = nodeArr[b];
	nodeArr[b] = tmp;
}

public static Node listPartition2(Node head ,int pivot){
	Node sH = null;//H代表head,T代表Tail,s表示small,e代表equal,b代表big。
	Node sT = null;
	Node eH = null;
	Node eT = null;
	Node bH = null;
	Node bT = null;
	Node next = null;//下一个节点
	//每一个节点都需要进行判断,然后分别挂到三个链表上。
	while(head !=null ){
		//下面两行是将head单独拿出进行比较,将head后面部分跟head分开,从而实现每一步单独比较
		next = head.next;
		head.next = null;
		if(head.value<pivot){
			if(sH == null){
				sH = head;
				sT = head;
			}else{
				sT.next = head;
				sT = head;
			}
		}else if(head.value == pivot){
			if(eH == null){
				eH = head;
				eT = head;
			}else{
				eT.next = head;
				eT = head;
			}
		}else{
			if(bH == null){
				bH = head;
				bT = head;
			}else{
				bT.next = head;
				bT = head;
			}
		}
		head = next;
	}
	//将小于和等于部分相连接,之所以若eT为空则让eT = sT,是为后面等于和大于部分相连接做准备。
	if(sT!= null) {
		sT.next = eH;
		eT = eT == null?sT:eT;
	}
	//全部连接
	if(eT != null){
		eT.next = bH;
	}
	//最后返回的内容中还需要进行三段中前两段是否为空的情况。
	return sH != null ? sH :eH != null ? eH :bH ;
}

			

复制含有随机指针节点的链表

  1. 使用hashmap
  2. 首先进行节点的遍历。1->1’->2->2’->3->3’->null,使得节点的下个节点都是本身的拷贝节点,可以通过random节点使其链接,同时找到对应的下一个节点,使得1’可以找到3‘,最后将最后串好的新老链表分开,就完成了整个链表的拷贝。
//第一种方法就是利用hashmap进行整个链表的复制
public static Node copyListWithRand1(Node head){
	HashMap<Node,Node> map = new HashMap<Node,Node>();
	Node cur = head;
	while(cur != null){
		map.put(cur,new Node(cur.value));
		cur = cur.next;
	}
	cur = head;
	//复制的主体部分,map中获取cur的值对应的key的下一个值等于cur的下一个值对应的key,利用键值对将原始的链表和要复制的链表联系起来,从而实现深度的拷贝。
	while(cur != null){
		map.get(cur).next = map.get(cur.next);
		map.get(cur).rand = map.get(cur.rand);
		cur = cur.next;
	}
	return map.get(head);
}

public static Node copyListWithRand2(Node head){
	if(head == null){
		return null;
	}
	Node cur =head;
	Node next = null;
	//复制所有的节点和链接
	while(cur != null){
		next = cur.next;
		cur.next = new Node(cur.value);
		cur.next.next = next;
		cur = next;
	}
	//下面的两个while循环一个是将rand连接进行复制,另外一个是将正常的连接进行复制
	cur = head;
	Node curCopy = null ;
	while(cur != null){
		next = cur.next.next;
		curCopy = cur.next;
		curCopy.rand = cur.rand != null ? cur.rand.next:null;
		cur = next;
	}
	Node res = head.next;
	cur = head;
	//split
	while(cur != null){
		next = cur.next.next;
		curCopy = cur.next;
		cur.next = next;
		curCopy.next = next != null ? next.next :null;
		cur = next;
	}
	return res;
}

两个单链表相交的一系列问题

判断是否有环:

  1. 利用hashset对两个单链表进行遍历,判断链表中的数值再hashset中是否存在,存在则有环,不存在则无环。
  2. 不用哈希表的话需要准备两个指针,快指针和慢指针,快指针一次走两步,慢指针一次走一步,如果有环,则快指针和慢指针一定会在环上相遇。快指针和慢指针相遇之后,将快指针调整到链表开始,此时快指针和慢指针均按一次一步,则会再第一个入环节点相遇。
    判断是否相交:
  3. 如果loop1为空,loop2为空,则将链表1所有节点放入map中,遍历链表2,查找map,是否在map中存在。
  4. 不使用map则先遍历map1,得到链表1的长度及链表1的最后一个节点。再遍历链表2的长度,及链表2的最后一个节点。如果end1跟end2相等,则比较链表长度,多的部分让多的链表先走多的部分,之后两个链表同时走,最终会走到首次相交的地方。
    单链表结构一个有环一个无环不可能相交。
  5. 有环的拓扑结构只可能有三种情况:如图所示。
  6. loop1,loop2,head1,head2就足够区分开三种情况.
    1)loop1等于loop2,则为情况2.砍掉环的部分,则又转化为之前的无环链表结构。
    2)loop1不等于loop2,则为结构1或结构3,loop1接着往下走,如果loop1往下走没遇见loop2,则不相交,如果loop1在遍历过程中遇到了loop2,则相交,返回loop1或loop2.
    此时只使用了有限的几个变量空间,没有使用hashmap。
public static Node getIntersectNode(Node head1,Node head2){
	if(head1 == null || head2 = null){
		return  null;
	}
	Node loop1 = getLoopNode(head1);
	Node loop2 = getLoopNode(head2);
	if(loop1 == null && loop2 == null){
		return noLoop(head1,head2);
	}
	if(loop1 != null && loop2 !=null){
		return bothLoop(head1,loop1,head2,loop2);
	}
	return null;
}
//根据结论获得的环的起始位置
public static Node getLoopNode(Node head){
	if(head == null || head.next == null || head.next.next){
		return null;
	}
	Node n1 = head.next;
	Node n2 = head.next.next;
	while(n1 != n2){
		if(n2.next == null || n2.next.next == null){
			return null;
		}
		n2 = n2.next.next;
		n1 = n1.next;
	}
	n2 = head;//n2从头重新开始走	
	while(n1 != n2){
		n1 = n1.next;
		n2 = n2.next;
	}
	return n1;
}

public static Node noLoop (Node head1,Node head2){
	if(head1== null || head2 == null){
		return null;
	}
	Node cur1 = head1;
	Node cur2 = head2;
	int n = 0;
	while(cur1.next != null){
		n++;
		cur1 = cur1.next;
	}
	while(cur2.next != null){
		n--;
		cur2 = cur2.next;
	}
	//两条链都走到最后,如果最后的数值不相同,则说明中间没有交点
	if(cur1 != cur2){
		return null;
	}
	//这个判断非常巧妙,判断出来哪条长之后直接对cur进行赋值
	cur1 = n >0 ?head1:head2;
	cur2 = cur1 == head1? head2:head1;
	//求一下绝对值
	n = Math.abs(n);
	//将多出来的长度先走完
	while(n!= 0 ){
		n--;
		cur1 = cur1.next;
	}
	while(cur1 != cur2){
		cur1 = cur1.next;
		cur2 = cur2.next;
	}
	return cur1;
}
public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2){
	Node cur1 = null ;
	Node cur2 = null;
	//有共同的环之后再将重新化作无环的情况去分析,从而寻找到交点起始位置。
	if( loop1 == loop2){
		cur1 = head1;
		cur2 = head2;
		int n = 0;
		while(cur1 != loop1){
			n++;
			cur1 = cur1.next;
		}
		while(cur2 != loop2){
			n--;
			cur2 = cur2.next;
		}
		cur1 = n>0? head1: head2;
		cur2 = cur1 == head1 ? head2:head1;
		n = Math.abs(n);
		while(n!= 0){
			n--;
			cur1 = cur1.next;
		}
		while(cur1 != cur2){
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}else{
		cur1 = loop1.next;
		//判断该环上loop1和loop2是不同的位置,loop1旋转一周必定会经过loop2,此时可以返回loop1
		while(cur1 != loop1){
			if(cur1 == loop2){
				return loop1;
			}
			cur1 = cur1.next;
		}
		return null;
	}
}


发布了24 篇原创文章 · 获赞 6 · 访问量 501

猜你喜欢

转载自blog.csdn.net/qq_35065720/article/details/104149150