面试算法 牛客题目 链表中的节点每k个一组翻转

1.题目:BM3 链表中的节点每k个一组翻转
描述
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。

数据范围: \ 0 \le n \le 2000 0≤n≤2000 , 1 \le k \le 20001≤k≤2000 ,链表中每个元素都满足 0 \le val \le 10000≤val≤1000
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如:
给定的链表是 1→2→3→4→5
对于 k = 2    2→1→4→3→5
对于 k = 3    3→2→1→4→5


2.算法:

1.暴力的单链表反转

2.递归的单链表反转


3.算法思想:

1.暴力思想:把这个题目拆成两个部分,k分段+局部反转。
时间复杂度O(n)
空间复杂度O(1)

想法是:每k组反转一次,这样的话,我们将整个链表看作是每k个一段,利用之前学的整个链表的反转以及反转部分链表进行操作。具体的步骤是(不是最优,是本身自己想到的觉得可行的想法):

2.递归思想:

1、先反转以 head 开头的 k 个元素

2、将第 k + 1 个元素作为 head 递归调用 fangfa_2 函数

3、将上述两个过程的结果连接起来


4.代码:

/*************************************************
作者:She001
时间:2022/9/19
题目:BM3 链表中的节点每k个一组翻转
描述
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。

数据范围: \ 0 \le n \le 2000 0≤n≤2000 , 1 \le k \le 20001≤k≤2000 ,链表中每个元素都满足 0 \le val \le 10000≤val≤1000
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如:
给定的链表是 1→2→3→4→5
对于 k = 2    2→1→4→3→5
对于 k = 3    3→2→1→4→5

***************************************************/

#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
	int i;
	node *next;	
}; 

void print(node * head)
{
	node* pp= head;
	while(pp!=NULL)
	{
		cout<<pp->i<<"  ";
		pp=pp->next;
	}
	cout<<endl;
	
} 


//算法的解释  链表 的 1 2 3 4 5 6 7   把 2  到  4    区间的链表反转  变成 1 4 3 2 5 6 7  
// 过程  开始
/*

//算法思想  : 
 
找到需要反转的开始节点  (第一个节点)n     和开始反转节点  n 之前的 节点 kk  
然后我们需要 开始 把这个节点 后面的节点反转  首先  把开始节点 n   连接   下一个节点 p1  的下一个节点  p2   ,
p1  连接  节点kk 的下一个节点      
节点kk  连接   节点 P1 
		   

 
算法过程:演示
 
1.     1  3  2  4  5  6  7     怎么变过来的  首先  2节点 指向   4 节点     3节点 指向 2 节点    1 节点 指向   3节点       
2.     1  4  3  2  5  6  7     
 



*/ 


node * fangfa_1_1(node * head,int m,int n)//head 链表的头节点,反转区间  m  第几个参数  ,  n 第几个参数  
{
	
  //加个表头  这样好返回链表的头  有可能他是反转第一个节点 
    node* res = new node;
    res->i=-1;
    res->next=head;
    res->next = head;
    //前序节点
    node* pre = res;
    //当前节点
    node* cur = head;
    //找到 m  和他之前的节点 
    for(int i = 1; i < m; i++){
        pre = cur;   
        cur = cur->next;
    }
    //循环之后的节点的意义
	//节点  pre  是 节点 n  之前的节点
	// 节点  cur  就是   节点  n
	  
    
    //从m 反转到 n   
    for(int i = m; i < n; i++){
        node* temp = cur->next; //新建一个node  指针 指向 节点 n  的下一个节点  
        cur->next = temp->next; //节点n   指向的下一个的节点   等于    之前指向的下一个节点 的下一个节点 。 
        temp->next = pre->next; //节点 temp 指向的下一个节点   等于  节点 pre 指向下一个节点    意思就是 把节点temp  插入 到  节点 temp 的下一个 
        pre->next = temp;  // 意思就是 把节点temp  插入 到  节点 temp 的下一个 
    }
    //返回去掉表头
    return res->next;
	
		
} 

//局外变量的直接暴力区间的反转 
node * fangfa_1(node* head,int n)//链表的头节点, n 需要反转的区间的大小
{
		//遍历链表,读取个数
		int a1=0;
		int a2=0;
		node * gg= head;
		int num=1;
		while(gg->next!=NULL)
		{
			num++;
			gg=gg->next;	
		}
		cout<<"链表的元素个数: "<<num<<endl;
		
		if(num<n)
		{
			cout<<"链表长度短了"<<endl;
			return head;	
		} 
		else
		{
			a1=1;//开始的第一个区间的开头坐标   因为是第一个参数 
			a2=n;//开始区间的结尾坐标 
			gg=head;//把链表初始化 
			while(a2<num-1)
			{
				gg= fangfa_1_1(gg,a1,a2);
			//	print(gg);
				a1=a2+1;
				a2=a2+n; 
				//cout<<"a1 = "<<a1<<endl;
				//cout<<"a2 = "<<a2<<endl; 
			}
			return gg;
		}
		
} 

//第二种方法: 递归 
//局部反转  返回节点 
node* fangfa_2(node* head, int k) {
	
    //找到每次翻转的尾部
    node* tail = head; 
    
    //遍历k次到尾部
    for(int i = 0; i <k; i++){ 
        //如果不足k到了链表尾,直接返回,不翻转
        if(tail == NULL) 
            return head;
        tail = tail->next; 
    }
    
    //翻转时需要的前序和当前节点
    node* pre = NULL; 
    node* cur = head;
    
    //在到达当前段尾节点前
    while(cur != tail){ 
        //翻转
        node* temp = cur->next; 
        cur->next = pre;
        pre = cur;
        cur = temp;
    }
    
    //结束的tail  是下一个区间的开始节点 
    
    //传过来的链表  第一个经过反转之后都变成了 ,最后一个节点 
    //当前尾指向下一段要翻转的链表
    head->next = fangfa_2(tail, k); 
    return pre;
}




int main()
{
	
	//建立单链表 
	node *a1=new node;
	node *a2=new node;
	node *a3=new node;
	node *a4=new node;
	node *a5=new node;
	node *a6=new node;
	node *a7=new node;
	
	a1->i=1;//链表节点的复制 
	a2->i=2;
	a3->i=3;
	a4->i=4;
	a5->i=5;
	a6->i=6;
	a7->i=7;
	
	a1->next=a2;//链表的连接 
	a2->next=a3;
	a3->next=a4;
	a4->next=a5;
	a5->next=a6;
	a6->next=a7;
	a7->next=NULL;
	
	//node * k=fangfa_1(a1,2);//反转链表的局部空间 
	//print(k);//打印数据 


	node * p=fangfa_2(a1,2);//反转链表的局部空间 
	print(p);//打印数据 
	

	return 	0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值