力扣算法题(6)链表,快慢指针,需要复习

本文详细探讨了链表的各种操作,包括翻转链表、删除重复元素、分隔链表以及旋转链表等。重点介绍了快慢指针在查找链表环、确定链表中点以及解决其他链表问题中的应用。此外,还讨论了如何通过递归和迭代方法处理链表问题,并强调了处理链表时需要注意的细节,如哨兵结点、头结点的处理以及链表的连接问题。
摘要由CSDN通过智能技术生成

链表

修改链表结构

206-Reverse Linked List

/*假设存在链表1->2->3->null,把它变成3->2->1->null
*在遍历列表时,将当前节点的next指向前一个元素。由于节点没有引用上一个节点,因此必须事先存储其前一个元素 *在更改引用之前,还需要另一个指针来存储下一个节点。最后要返回新的头引用,所以建造是从屁股走向头???? * */ //链表,官方解答 //方法一:迭代 /*设置一个指针curr表示当前遍历到的node,初始值为head;
*设置一格指针prev表示当前遍历的node和前一个node,初始值为null *使用一个while循环,当curr指向的node不为null时,一直遍历完所有node; *每次遍历时,设置一个临时指针变量temp,用来暂存curr.next指向的node; 接着开始反转链表,将当前当前node的下一个node指针的值设置为前一个node指针的值,设置curr.next=prev; *重设前一个node指针的值,设置prev=curr *重设当前node指针的值,设置curr=temp/

public ListNode reverseList(ListNode head) {
   
	ListNode prev=null;//新链表的前一个
	ListNode curr=head;//curr下一个要插入的
	while(curr!=null) {
   
		//头插法
		ListNode nextTemp=curr.next;//用nextTemp记录来下一个要放进来的,curr是我们正在插入的
		curr.next=prev;//把我们正在 插入的curr指向老链接的前一个prev,头节点反转后是最后一个    老链表nexTemp<-curr<-prev
		prev=curr;//插入成功后,pre指向新插入的结点,也就就是下一个插入的next
		curr=nextTemp;//下一个要插入的是我们记录下来的原链接下一个
	}
	return prev;
}

//方法2,递归,递归的关键在于反向工作。假设链表的其余部分已经被反转,如何反转前面的部分。
/原始时nk->nk+1,反转需要nk+1->nk,所以nk.next.next=nk,nk.next=null *
需要注意的是n1的下一个节点必须指向null,如果忽略会产生环
/

public ListNode reverseList01(ListNode head) {
   
	if(head==null||head.next==null) {
   
		return head;
	}
	ListNode newHead=reverseList01(head.next);
	head.next.next=head;
	head.next=null;
	return newHead;
}

//链表的题目不会可以画图

92-Reverse Linked List II (翻转链表中m-n之间的元素,掌握)这道题很重要!!!!!!!!!!!!!!

92翻转链表中m-n之间的元素,这道题很重要!!!!!!!!!!!!!!!!!!!!!!!
/给单链表的头指针head和两个整数left和right,其中left<=right,反转从位置left到right的链表节点,返回反转后的链表/
//要把不转的地方存起来,如何指向不转的端点,遍历left次

//官方解法,双指针,头插法!!!!!!!!!!!!!!!!! /*
定义两个指针,分别称为g(guard守卫)和p(point)
首先根据方法的参数m确定g和p的位置,将g移动到第一个要反转的节点前面,将p移动到第一个要反转的节点的位置上。将p后面的元素删除,然后添加到g的后面,也就是头插法。

public ListNode reverseBetween(ListNode head,int m,int n) {
   
	//定义一个dummyHead指向这个链表,方便处理
	ListNode dummyHead=new ListNode(0);
	dummyHead.next=head;
	//初始化指针
	ListNode g=dummyHead;//g指向head
	ListNode p=dummyHead.next;//p此时在head这里,p移动到第一个要反转的节点的位置
	//将指针移到相应的位置
	for(int step=0;step<m-1;step++) {
   //跳了0-m-2次,也就是m-1次
		g=g.next;//g在要反转的节点前面
		p=p.next;//p在要反转的节点位置
	}
	//头插法插入节点
	for(int i=0;i<n-m;i++) {
   
		ListNode removed=p.next;//要移走的节点先存起来
		p.next=p.next.next;//把要移走的节点去掉
		//头插
		removed.next=g.next;//把要移走的节点放在左端点(不移动)的右边
		g.next=removed;//原始位置左端的指针指向要移走的节点
	}
	return dummyHead.next;//最后返回头结点
}

24-Swap Nodes in Pairs (92题代码的应用)

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表

这个写错了,没有检查出来,调用方法时出现越界情况。
越界是因为最后换两个的时候,next.next不存在
错误原因,且是&&,函数名不是03,换了以后应该有一个结点连接一下前面的,做链表函数,不能忘了连上前面的不然断了,脑子不行

//24
//不用函数的非递归算法,要有交换的两个数据的前一个才行

private ListNode reversebetween(ListNode head) {
   	
	ListNode dummyHead=new ListNode(0);//定义一个新的结点用来存
	dummyHead.next=head;
	ListNode p=head;
	ListNode q=head.next; 
	ListNode temp=q.next; //这里把剩下的存起来
    //这一句有问题
	
	
	p.next=null;
	q.next=p;
	p.next=temp;
	return q;//返回的是交换后的头结点
}
//把函数改成交换head为头的前两个结点
public ListNode swapPairs03(ListNode head) {
   
	 ListNode dummyHead=new ListNode(0);//定义一个新的结点用来存
		dummyHead.next=head;
		ListNode p=head;
	    ListNode pre = new ListNode(0);
	    ListNode result=pre;
	    pre.next=head;
		while(p!=null && p.next!=null) {
   
	        p=reversebetween(p);//换了应该连接前面的
	        pre.next=p;//连上前面的
	        
	        pre=p.next;//pre移到要换位置的前一个地方
	        p=p.next.next;//p移到下一个要换的地方
		}
		return result.next;
}
private ListNode reversebetween(ListNode head) {
   	
	ListNode dummyHead=new ListNode(0);//定义一个新的结点用来存
	dummyHead.next=head;
	ListNode p=head;
	ListNode q=head.next; 
	ListNode temp=null;
    if(q.next!=null){
   
      temp=q.next; 
    }
	
	q.next=p;
	p.next=temp;
	return q;//返回的是新头
}
//把函数改成交换head为头的前两个结点
public ListNode swapPairs03(ListNode head) {
   
    ListNode dummyHead=new ListNode(0);//定义一个新的结点用来存
	dummyHead.next=head;
	ListNode p=head;
    ListNode q=head.next;//这一定是头
    if(q==null)return head;
	while(p!=null & p.next!=null) {
   
        System.out.println(p.val);
        p=reversebetween(p);//换了应该连接前面的
        q.next.next=p;//老头的下一个的下一个是新头
        q=p;//更新头
		p=p.next.next;
        System.out.println(p.val);
	}
    System.out.println(p==null);
	return head.next;
}

//递归,递归的终止条件是链表中没有节点,或者链表中只有一个节点,此时无法进行交换
//链表中至少有两个节点,两两交换后,原始链表的头结点变成新的链表的第二个节点, //原始链表中的第二个节点变成第二个节点
//链表中的其余递归两两交换后,更新节点之间的指针关系,即可完成整个链表的两两交换

//用head表示原始链表的头节点,用newHead表示新的链表头节点,原始链表的第二个节点。
//原始链表中的其余节点的头节点newHead.next.递归令head.next=swapPairs(newHead.next),表示将其余节点两两交换

public ListNode swapPairs01(ListNode head) {
   
	if(head==null||head.next==null) {
   
		return head;
	}
	//这就是一个头插法,把老head取下来,放在newHead后面
	ListNode newHead=head.next;
	head.next=swapPairs01(newHead.next);//新head的下一个是.next.next
	newHead.next=head;
	return newHead;
}

//迭代 //创建哨兵结点dummyHead,dummyHead.next=head,
//令temp表示当前到达的结点,初始时temp=dummyHead.每次需要交换temp后面的两个结点
//如果temp的后面没有结点或者只有一个结点,则没有更多的结点需要交换,否则获得temp后面的两个结点node1,node2
//交换之前的结点关系是temp->node2->node1,因此需要进行如下操作 //temp.next=node2
//node1.next=node2.next //node2.next=node1
//结束后temp->node2->node1再令temp=nodel,

public ListNode swapPairs02(ListNode head) {
   
	
	ListNode dummyHead=new ListNode(0);
	dummyHead.next=head;
	//temp表示当前到达的结点
	ListNode temp=dummyHead;
	while(temp.next!=null&&temp.next.next!=null) {
   
		ListNode node1=temp.next;
		ListNode node2=temp.next.next;
		//temp-node1-node2变成temp-node2-node1
		temp.next=node2;
		node1.next=node2.next;
		node2.next=node1;
		//结束后node1变成temp
		temp=node1;
	}
	return dummyHead.next;
}

//递归的本质就是不断重复相同的事情,而不是去思考完整的调用栈,一级又一级,无从下手。
//关注一级调用小单元的情况,关注1,返回值2,调用单元做了什么3,终止条件

25-Reverse Nodes in k-Group (24题的升级)

//25 Reverse NOdes in k-Group //k个一组翻转链表 //如果结点总数不是k的整数倍,将剩余的结点保持原有顺序。

//使用常数额外空间的算法来解决问题,不能单纯改变节点内部的值,需要实际进行结点交换 //需要有一个判断,有没有k个结点
//这个题目,关于如何判断有没有k个结点,只想到了顺序遍历。。

这个题目好好复习,完全没有自己的思路
1,链表区分为已翻转部分+待翻转部分+未翻转部分
2,翻转后返回新的头结点,然后原始start此时就是尾结点

//每次翻转前,要确定翻转链表的范围,这个必须通过k次循环来确定 //需记录翻转链表前驱和后继,方便翻转完后把已翻转和未翻转部分连接起来
//初始需要两个变量pre和end,pre代表翻转链表的前驱,end表示待翻转链表的末尾
//经过k次循环,end到达末尾,记录待翻转链表的后继next=end.next
//翻转链表,然后将三部分链表连接起来,然后重置pre和end指针,然后进入下一次循环
//当翻转部分不足k时,在定位end完成后,end==null,已经到达了末尾,说明题目已经完成,直接返回结果

public ListNode reverseKGroup(ListNode head,int k) {
   
	
	ListNode dummy =new ListNode(0);
	dummy.next=head;
	//pre每次指向要翻转的链表的头结点的上一个结点,end指向每次要翻转的链表的尾结点
	ListNode pre=dummy;
	ListNode end=dummy;
	
	while(end.next!=null) {
   
		for(int i=0;i<k&&end!=null;i++) end=end.next;
		//经过k次循环,end到达要翻转的链表的尾结点
		if(end==null)break;
		//记录要翻转链表的头结点
		ListNode start=pre.next;
		ListNode next=end.next;//next记录下一次开始的地方
		
		//断开链表
		end.next=null;
		
		//翻转链表,pre.next指向翻转后的链表。1->2,变成2—>1.dummy->2->1
		pre.next=reverse(start);
		//反转后结点变到最后,通过.next把断开的链表重新连接。
		start.next=next;
		//将fre换成下次要翻转的链表的头结点的上一个结点。即start
		pre=start;
		//翻转结束,将end置为下次要翻转的链表的头结点的上一个结点,即start
		end=start;
	}
	return dummy.next;
}

//链表翻转
//例子:head:1->2->3->4
public ListNode reverse(ListNode head) {
   
	
	//单链表为空或只有一个结点,直接返回原单链表
	if(head==null||head.next==null) {
   
		return head;
	}
	//前一个结点指针
	ListNode preNode=null;
	//当前结点指针
	ListNode curNode=head;
	//下一个结点指针
	ListNode nextNode=null;
	
	while(curNode!=null) {
   
		//nextNode指向下一个结点,保存
		nextNode =curNode.next;
		//将当前结点next域指向前一个结点
		curNode.next=preNode;
		//preNode指针向后移动,preNode指向当前结点
		preNode=curNode;
		//curNode指向向后移动,下一个结点变成当前结点
		curNode=nextNode;
	}
	return preNode;
}

//25把这个题目写成函数形式 //复杂度过高,没有办法运行,因为有一个点没有运用到,就是反转后,头就变成尾,然后返会新的头结点可以少用几个指针,改!!!!!!!!!!!

public ListNode reverseKGroup1(ListNode head, int k) {
   
	
	//每次截取一段,从start到end,调用函数反转。
	ListNode start=head;
	ListNode end=head;
	ListNode pre=new ListNode(0);
	pre.next=head;

	ListNode next=end.next;
	
	for(int i=0;i<k-1;i++) {
   if(end==null)return head;end=end.next;}
	
	while(end!=null) {
   
		//如果k是2,end跳一次,end此时处于需要转换部分的尾结点
		next=end.next;//存一下后面要继续翻转的部分
		
		//翻转
		end.next=null;
		start=reverse1(start);
		//start此时处于要reverse部分的首位,pre指向要转换的部分
		pre.next=start;
		
		pre=end;//下一段的前一个,也就是前一段的最后一个
		start=next;
		end=next;
		for(int i=0;i<k-1;i++) {
   if(end==null)return head;end=end.next;}
	}
	return head;
}
//函数返回reverse后的head
private ListNode reverse1(ListNode head) {
   
	
	//这个函数,翻转一个k长的链表
	//有递归和非递归法
	ListNode start=null;//这是初始头
	ListNode DummyHead=head;
	//头插法
	while(DummyHead!=null) {
   
		DummyHead.next=start;
		start=DummyHead;
	}
	return start;
}

//25不用函数,尝试自己写一下,k个一组翻转列表
//失败的尝试
//先写一下题目的算法:链表分为三部分,pre已翻转last,start正在翻转end,left未翻转
//需要五个指针,来用于记方便后面连接
//在一个while循环里,首先判断end不是空就可以进入循环,翻转翻转部分,更新end和start,连接三段
//更新四个指针

public ListNode reverseKGroup02(ListNode head, int k) {
   
	ListNode end=new ListNode(0);
	ListNode pre=new ListNode(0);
	
	//初始化五个指针的值
	pre.next=head;
	
	ListNode start=head;
	end.next=head;
	for(int i=0;i<k;i++) {
   if(end==null)return pre.next;end=end.next;}//正在翻转部分的end
	ListNode left=end.next;
	ListNode last=pre;//此时都没翻转,已翻转部分为空
	
	while(left!=null) {
   //当还有没翻转的
		//反转start到end
		ListNode second =null;
		//头插法
		//头插法需要套一个循环这里可能有点繁琐了
		
		//新一轮的起点
	
	}
	return head;
}

//没有写完,25题依然不是很会需要复习

//25跟着继续学习一次,这道题好好学习,依然不会写

public ListNode reverseKGroup03(ListNode head, int k) {
   
	
	//为了最后返回
	ListNode dummy=new ListNode(0);
	dummy.next=head;
	
	ListNode pre=dummy;
	ListNode end=dummy;
	
	while(end.next!=null) {
   
		for(int i=0;i<k&&end!=null;i++)end=end.next;//这里写的很简洁
		if(end==null)break;
		ListNode start=pre.next;
		ListNode next= end.next;
		end.next=null;
		//已翻转部分连接正翻转的部分
		pre.next=reverse(start);
		//正翻转部分连接未翻转部分
		start.next=next;//老头现在变成了尾!!!!!!!!!!!!!!!
		
		pre=start;//新的尾就是原来的头
		end=pre;
	}
	
	return dummy.next;
}

83-Remove Duplicates from Sorted List

83—Remove Duplicates from Sorted List
删除排序链表中的重复元素,存在一个按升序排列的链表,给你这个链表的头结点,删除所有重复元素,每个元素只出现一次。112变成了2

ListNode的一个属性就是next,所以不用担心next没有,但是next.next可能没有

public ListNode deleteDuplicates(ListNode head) {
   
	
	ListNode dummyHead=new ListNode(0);
	dummyHead.next=head;
	ListNode pre=dummyHead;
	
	//这样写好像会默认跳了pre本身
	while(pre.next!=null&&pre.next.next!=null) {
   
		if(pre.next.val==pre.next.next.val) {
   
			pre.next=pre.next.next;//这个一下跳了两个
		}else</
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值