算法探究6-K个一组翻转链表

常规

题目来源:LeetCode-25-K个一组翻转链表
思路:

  1. 给已知链表加一个头节点 H a i r Hair Hair(官方的代码还挺有趣)
  2. 找出需要翻转的链表的头指针和尾指针(尾指针需要从头指针向后扫描K个,若不满足,则不翻转)
  3. 保存尾指针的下一个节点地址 n e x nex nex,不然等这一段翻转结束后链表就串不起来了
  4. 进入翻转函数进行翻转,这里使用了C++17的一个新功能,返回了一个用 t i e ( ) tie() tie()函数组合的 p a i r pair pair(一般函数的返回值只能是一个值,而这里同时返回翻转后链表的头指针和尾指针)
  5. 翻转结束后将链表重新链接, h e a d head head是头指针的后继,而 t a i l tail tail n e x nex nex的前驱
  6. 更新 p r e pre pre h e a d head head( p r e pre pre h e a d head head的前驱, h e a d head head为待翻转的第一个元素)
  7. 翻转函数种所做的事情其实就是对传入的链表进行翻转,很像循环链表的操作过程,在这里需要注意要修改指针值时,必须先将指针进行保存,不然该元素就永远找不见了。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* hair = new ListNode(0);
        hair->next = head;
        ListNode* pre = hair;
        while(head){
            ListNode* tail = pre;
            for(int i = 0;i < k;i++){
                tail = tail->next;
                if(tail == NULL)
                    return hair->next;
            }
            ListNode* nex = tail->next;
            tie(head,tail) = MyReverse(head,tail);
            pre->next = head;
            tail->next = nex;
            pre = tail;
            head = tail->next;
        }
        return hair->next;
    }
    pair<ListNode*,ListNode*> MyReverse(ListNode* head,ListNode* tail){
        ListNode* prev = tail->next;
        ListNode* p = head;
        while(prev != tail){
            ListNode* nex = p->next;
            p->next = prev;
            prev = p;
            p = nex;
        }
        return {tail,head};
    }
};

在这里插入图片描述

静态链表

题目来源:PTA-Reversing Linked List
分析:其实思路和常规链表一样,只是数据是用数组保存的

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int MAXSIZE = 1e5 + 1;
struct Node{
    int Data;
    int Next;
};
vector<Node> List;
int ADDRESS,N,K;
void Init(){
    cin >> ADDRESS >> N >> K;
    List.resize(MAXSIZE);
    for(int i = 0;i < N;i++){
        int Index,Data,Add;
        cin >> Index >> Data >> Add;
        List[Index].Data = Data;
        List[Index].Next = Add;
    }
}
pair<int,int> MyReverse(int head,int tail){
    int prev = List[tail].Next;
    int p = head;
    while(prev != tail){
        int nex = List[p].Next;
        List[p].Next = prev;
        prev = p;
        p = nex;
    }
    return {tail,head};
}
void ReverseKGroup(){
    int pre = 1e5;
    List[pre].Next = ADDRESS;
    int head = ADDRESS;
    while(head != -1){
        int tail = pre;
        for(int i = 0;i < K;i++){
            tail = List[tail].Next;
            if(tail == -1)
                return;
        }
        int nex = List[tail].Next;
        tie(head,tail) = MyReverse(head,tail);
        List[pre].Next = head;
        List[tail].Next = nex;
        pre = tail;
        head = List[tail].Next;
    }
}
void Print(){
    int Pos = List[1e5].Next;
    while(Pos != -1){
        cout << setw(5) << setfill('0') << Pos << " " ;
        cout << List[Pos].Data << " " ;
        if(List[Pos].Next == -1)
            cout << List[Pos].Next << endl;
        else
            cout << setw(5) << setfill('0') << List[Pos].Next << endl;
        Pos = List[Pos].Next;
    }
}
int main(){
    Init();
    ReverseKGroup();
    Print();
}

在这里插入图片描述
将程序中的 v e c t o r vector vector容器改为 N o d e Node Node类型的数组,其空间复杂度会有显著降低,时间复杂度却会上升。由于题目允许的空间复杂度较高,而时间复杂度较低,所以可以使用容器来用空间换时间。
在这里插入图片描述
另一种方式是将各个节点的地址进行保存,使用 r e v e r s e ( ) reverse() reverse()函数翻转地址。其中 r e v e r s e ( ) reverse() reverse()函数的两个参数为 [ f i r s t , l a s t ) [first,last) [first,last),左闭右开区间

#include<iostream>
#include<stdio.h>
#include<algorithm>

using namespace std;

#define MAXSIZE 1000010

struct node		//使用顺序表存储data和下一个地址next
{
	int data;
	int next;
}node[MAXSIZE];

int List[MAXSIZE];
int main()
{
	int first, n, K; //输入头地址 和 n,k;
	int Address, Data, Next;

	cin >> first >> n >> K;
	for (int i=0; i < n; i++)
	{
		cin >> Address >> Data >> Next;
		node[Address].data = Data;
		node[Address].next = Next;
	}
	
	int j = 0;  //j用来存储能够首尾相连的节点数
	int p = first;   //p指示当前结点
	while (p != -1)	//保存所有节点的地址,便于后边翻转
	{
		List[j++] = p;
		p = node[p].next;
	}
	int i = 0;
	while (i + K <= j)
	{
		reverse(&List[i],&List[i+K]);
		i=i+K;
	}
	for (i = 0; i < j - 1; i++)
	{
		printf("%05d %d %05d\n",List[i],node[List[i]].data,List[i+1]);
	}
	printf("%05d %d -1\n",List[i],node[List[i]].data);
	return 0;
}


在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值