从2021年8月7日开始一天6道题之第一题:重排链表

前言:
为了更好的自我监督,为了锻炼自己的代码能力,开始刷题,一共100道题,一天6道题,假期还剩下20天,还可以练完。
上午两道题 下午两道题 晚上两道题。

题目描述:
在这里插入图片描述
重排链表 算法思路:
(1)首先找到链表的中间结点
(2)对链表的后半部分进行逆序反转
(3)把链表的前半部分子链表与逆序后的后半部分子链表进行合并,合并的思路为:分别从两个 链表各取一个结点进行合并

main函数:
int main(){
ListNode *head=createByTail();
Solution().reorderList(head);
displayLink(head);
return 0;
}

首先要利用尾插法创建链表,在这里题目要求使用不带头结点的单链表进行操作,所以像之前的ListNode *head=new ListNode;head->next=NULL之类的代码不再使用,而是直接ListNode *head;head=NULL;即可,因为带头结点的单链表是肯定要new一个新的ListNode 但是无头结点就不用。
所以有以下代码:

ListNode *creatByTail(){
	ListNode *head;
	ListNode *p;
	ListNode *tail;
	int n=0,num;
	int len;
	cin>>len;
	head=NULL;
	while(n<len && cin>>num){
		p=new ListNode;
		p->val=num;
		p->next=NULL;
		n=n+1;
		if(n==1){
			head=p;
		}
		else{
			tail->next=p;
			}
		tail=p;
}
	return head;
}

创建链表之后就要对链表进行操作了,再来复习一下算法的思路:
(1)首先利用快慢指针找出链表的中间结点
(2)然后将中间结点之后的链表部分进行逆序反转操作
(3)将包含中间结点在内的前半部分链表作为list1,逆序之后的后部分链表作为list2,将list1和list2进行交叉合并即可。

首先是利用快慢指针找出链表的中间结点,原理就是快指针每次走两步,慢指针每次走一步,遍历链表,当fast->next=NULL或者fast->next->next==NUll的时候说明链表已经要遍历完,此时慢指针正好走到链表的中间位置。代码如下:

ListNode *middleNode(ListNode *head){
	ListNode *fast=head;
	ListNode *slow=head;
	while(fast->next!=NULL && fast->next->next!=NULL){
		slow=slow->next;
		fast=fast->next->next;
		}
	return slow;
}

利用上面的代码就可以找到链表的中间结点,找到之后我们说要将中间结点的后面部分链表进行逆序,逆序的思想我参考了这篇文章逆序思想
我觉得作者写的非常好,在这里我自己重新学习一遍。
逆序思想:
(1)如果是空链表即head==NULL的情况,直接返回。
(2)if head!=NULL;那么要利用三个指针,ListNode *prev=NULL;(用来指向前指针)
ListNode *curr=head;(用来指向当前指针)
ListNode *nextTemp=curr->next;(用来指向后指针)
(3)遍历链表,循环的判定因子是curr是否为NULL,为NULL表示达到链表结尾跳出循环,否则在链表中执行循环内逻辑:
将curr指向的当前结点的下一个结点指向prev,即curr->next=prev,此时prev=NULL,那么curr的下一个结点就为空了,表示它现在是最后一个结点了。然后将prev指针指向curr,将curr指向nextTemp,即prev=curr;curr=nextTemp;然后再进行下一次循环即可。
代码如下:

ListNode *reverseLink(ListNode *head){
	ListNode *prev=NULL;
	ListNode *curr=head;
	while(curr!=NULL){
		ListNode nextTemp=curr->next;
		curr->next=prev;
		prev=curr;
		curr=nextTemp;
	}
	return prev;
}

将中间结点之后的链表进行逆序反转之后就要进行合并了,在合并之前来看看我们是如何将原本的一个链表变为两个链表的:

			ListNode *mid=middleNode(head);
			ListNode *list1=head;
			ListNode *list2=mid->next;
			mid->next=NULL;
			list2=reverseList(list2);
		}

首先找到中间结点mid,然后将原本链表的head赋值给list1,将mid->next赋值为list2,也就是将中间结点指向的下一个结点赋值为list2。然后将mid->next赋值为NULL,说明list1到mid处就完了,此时再反转list2即可。

来看最后的合并操作,合并操作非常简单,就是利用两个临时变量,一个用来遍历链表1中的元素,一个用来遍历链表2中的元素,首先将链表2的第一个元素插入到链表1的第一个元素后,然后将链表1的第二个元素插入到链表2的第一个元素后面,代码如下:

void mergeList(LixtNode *list1,ListNode *list2){
	ListNode *list1_temp;
	ListNode *list2_temp;
	while(list1!=NULL && list2!=NULL){
		list1_temp=list1->next;
		list2_temp=list2->next;

		list1->next=list2;
		list1=list1_temp;

		list2->next=list1;
		list2=list2_temp;
		}
}
		

完整代码如下:

#include<iostream>
using namespace std;

//重排链表 算法思路:
//(1)首先找到链表的中间结点
//(2)对链表的后半部分进行逆序反转 
//(3)把链表的前半部分子链表与逆序后的后半部分子链表进行合并
//合并的思路为:分别从两个 链表各取一个结点进行合并 
struct ListNode{
	int val;
	ListNode *next;
	
}; 

class Solution{
	public:
		void reorderList(ListNode *head){
			if(head == NULL){
				return;
			}
			ListNode *mid=middleNode(head);//找出链表的中间结点
			ListNode *list1=head;
			ListNode *list2=mid->next;
			mid->next=NULL;
			list2=reverseList(list2);
			mergeList(list1,list2);
		}
		
		ListNode *middleNode(ListNode *head){
			ListNode *slow =head;
			ListNode *fast =head;
			while(fast->next!=NULL && fast->next->next!=NULL){
				slow=slow->next;
				fast=fast->next->next;
			}
			return slow;
		} 
		ListNode *reverseList(ListNode *head){
			ListNode *prev=NULL;
			ListNode *curr=head;
			while(curr!=NULL){
				ListNode *nextTemp=curr->next;
				curr->next=prev;
				prev=curr;
				curr=nextTemp;
			}
			return prev;
		}
		void mergeList(ListNode *list1,ListNode *list2){
			ListNode *list1_temp;
			ListNode *list2_temp;
			while(list1!=NULL && list2!=NULL){
				list1_temp=list1->next;
				list2_temp=list2->next;
				
				list1->next=list2;
				list1=list1_temp;
				
				list2->next=list1;
				list2=list2_temp; 
			}
		}
		
};


ListNode *createByTail(){
	
	ListNode *head;
	ListNode *p,*tail;
	int n=0,num;
	int len;
	cin>>len;
	head=NULL;
	while(n<len && cin>>num){
		p=new ListNode;
		p->val=num;
		p->next=NULL;
		n=n+1;
		if(n==1){
			head=p;
		}
		else{
			tail->next=p;
		}
		tail=p;
	} 
	
	return head;
}


void displayLink(ListNode *head){
	ListNode *p;
	p=head;
	cout<<"head-->";
	while(p!=NULL){
		
		cout<<p->val<<"-->";
		p=p->next;
	}
	cout<<"tail\n";
}


int main(){
	
	ListNode *head=createByTail();
	Solution().reorderList(head);
	displayLink(head);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值