链表的应用进阶操作

链表相关算法问题解析

查找倒数第k个数

用尽可能快的方法返回带头节点单链表中倒数第 k 个结点的地址,如果不存在,则返回 NULL。

#include <iostream>
using namespace std;

typedef int datatype;
typedef struct ListNode {
    datatype data;
    struct ListNode *next;
}LNode,*LinkList;

/*创建一个非空的带头节点单链表 */
void CreatbyQueue(LinkList &L);

/*查找并返回带头节点单链表中倒数第k个节点,如果不存在,则返回NULL。*/
LNode* SearchKthToLast(LinkList L,int k);



/*查找并返回带头节点单链表中倒数第k个节点,如果不存在,则返回NULL。*/
LNode* SearchKthToLast(LinkList L,int k)
{

    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
if (L == NULL || k <= 0) {
        return NULL;
    }

    LNode *pAhead = L->next; // Start from the first actual data node
    LNode *pBehind = L->next;
    int count = 0;

    // Move pAhead k nodes ahead
    while (pAhead != NULL && count < k) {
        pAhead = pAhead->next;
        ++count;
    }

    // If we didn't move k nodes ahead, k is larger than the length of the list
    if (count < k) {
        return NULL;
    }

    // Move pAhead and pBehind at the same pace
    while (pAhead != NULL) {
        pAhead = pAhead->next;
        pBehind = pBehind->next;
    }

    // pBehind is now the kth to last node
    return pBehind;

    /********** End **********/
    
}

链表逆置

1、单链表的插入
2、单链表的删除

#include <iostream>
using namespace std;

typedef int datatype;
typedef struct ListNode {
    datatype data;
    struct ListNode *next;
}LNode,*LinkList;

 /*单链表创建*/
void CreateList(LinkList &L);

/*其中L是用户传入的带头节点单链表的头指针,L是引用类型;函数Reverse将链表L逆置。*/
void Reverse( LinkList &L);

/*顺序输出链表每个结点,每个结点元素值以空格符间隔,以换行符结束。 */
void DispList(LinkList L);


/*其中L是用户传入的带头节点单链表的头指针,L是引用类型;函数Reverse将链表L逆置。*/
void Reverse( LinkList &L){
    
   // 请在这里补充代码,完成本关任务
    /********** Begin *********/
LNode *current = L->next; // 指向第一个数据结点
    LNode *prev = NULL;       // 初始化前一个结点指针为 NULL
    LNode *next = NULL;       // 初始化下一个结点指针为 NULL

    // 遍历链表,逐个结点逆置
    while (current != NULL) {
        next = current->next; // 保存下一个结点
        current->next = prev; // 逆置当前结点的 next 指针
        prev = current;       // 前一个结点移动到当前结点
        current = next;       // 当前结点移动到下一个结点
    }

    // 最后,将头结点的 next 指针指向新的第一个结点(原来的最后一个结点)
    L->next = prev;

    /********** End **********/
}

有序单链表的就地合并

相关知识

单链表的存储;
单链表的基本操作。

#include <iostream>
using namespace std;

typedef int datatype;
typedef struct ListNode {
    datatype data;
    struct ListNode *next;
}LNode,*LinkList;


/*创建一个非空的带头节点单链表,默认输入数据是升序的。 */
void CreatbyQueue(LinkList &L);

/*已知两个带头节点的单链表L1和L2中的结点值均已按严格升序排序,
设计一个算法,将L1和L2就地合并成一个严格升序的带头节点单链表,合
并之后用L1记录新的带头节点单链表的头指针,L2单链表为空。 */
void MergeAscend(LinkList &L1,LinkList & L2);

/*顺序输出链表每个结点,每个结点元素值以逗号间隔,以换行符结束。 */
void DispList(LinkList L);


/*将L1和L2合并成一个升序的带头节点单链表,并用L1记录新的带头节点单链表的头指针;*/
void MergeAscend(LinkList &L1,LinkList &L2)
{
        // 请在这里补充代码,完成本关任务
    /********** Begin *********/
LinkList R,P,Q,temp;
R=L1;             //R指向表位
P=L1->next;       // p指向L1
L1->next=NULL;
Q=L2->next;    //Q point L1
L2->next=NULL;
while(P&&Q)
{
    if(P->data<Q->data)
    {
        R->next=P;
        R=P;
        P=P->next;
    }
    else if(P->data>Q->data)
    {
        R->next=Q;
        R=Q;
        Q=Q->next;
    }
    else{
        R->next=P;
        R=P;
        P=P->next;//两数相等
        temp=Q;
        Q=Q->next;
        delete temp;
    }
}
if(P) R->next=P;
if(Q) R->next=Q;

    /********** End **********/  
}

两个一元多项式异地相加

利用带头节点单链表实现一元稀疏多项式的加法运算(C=A+B),实现异地相加,A,B 保持不变。

#include <iostream>
using namespace std;


typedef struct Node
{
    int coef;  //系数
    int expo;  //指数
} datatype;   //表示多项式的一项

typedef struct ListNode
{
    datatype data;
    struct ListNode *next;
} LNode,*LinkList;

bool InitList(LinkList &L);//初始化一个为空的带头结点单链表

/*创建一个非空的带头节点单链表,默认输入数据是升序的。 */
void CreatbyQueue(LinkList &L);

/*实现多项式加法C=A+B,A和B保持不变*/
void Add(LinkList &A,LinkList &B,LinkList &C);

/*顺序输出链表每个结点,每个结点元素值以逗号间隔,以换行符结束。 */
void DispList(LinkList L);


/*实现多项式加法C=A+B,A和B保持不变*/
void Add(LinkList &A,LinkList &B,LinkList &C)
{
      // 请在这里补充代码,完成本关任务
    /********** Begin *********/
LNode *pa = A->next; // 指向链表 A 的第一个数据节点
    LNode *pb = B->next; // 指向链表 B 的第一个数据节点
    LNode *pc = C = new LNode; // 创建结果链表 C 的头节点,并让 pc 指向它
    pc->next = NULL;

    // 遍历链表 A 和 B
    while (pa != NULL && pb != NULL) {
        if (pa->data.expo < pb->data.expo) { // 如果 A 的指数小于 B 的指数
            pc->next = new LNode; // 在 C 中创建一个新的节点
            pc = pc->next;
            pc->data = pa->data; // 将 A 的当前节点数据复制到 C
            pa = pa->next; // A 指针后移
        } else if (pa->data.expo > pb->data.expo) { // 如果 A 的指数大于 B 的指数
            pc->next = new LNode; // 在 C 中创建一个新的节点
            pc = pc->next;
            pc->data = pb->data; // 将 B 的当前节点数据复制到 C
            pb = pb->next; // B 指针后移
        } else { // 如果 A 和 B 的指数相等
            int sum = pa->data.coef + pb->data.coef; // 将系数相加
            if (sum != 0) { // 如果系数不为 0,则添加到结果链表 C
                pc->next = new LNode;
                pc = pc->next;
                pc->data.coef = sum;
                pc->data.expo = pa->data.expo;
            }
            pa = pa->next; // A 指针后移
            pb = pb->next; // B 指针后移
        }
    }

    // 将剩余的 A 或 B 的节点添加到 C
    while (pa != NULL) {
        pc->next = new LNode;
        pc = pc->next;
        pc->data = pa->data;
        pa = pa->next;
    }
    while (pb != NULL) {
        pc->next = new LNode;
        pc = pc->next;
        pc->data = pb->data;
        pb = pb->next;
    }

    pc->next = NULL; // 确保结果链表的最后一个节点的 next 指针为 NULL

    /********** End **********/  
}

约瑟夫环问题

约瑟夫环问题的描述是:编号为 1,2,…,n 的n 个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限m,从第一个人开始按顺时针方向自1开始顺序报数,报到 m 时停止报数。报 m 的人出列,将他的密码作为一个新的 m 值,从他在顺时针方向上的下一个人开始重新从 1 报数,如此下去,直至所有人全部出列为止。请设计一个程序求出列顺序。

#include<iostream>
using namespace std;

#define ERROR NULL
typedef struct Node
{
    int n;  //个人的编号
    int m;  //手中的密码
} ElemType; //ElemType 定义为结构体类型,分别记录每个人的编号以及手中的密码

typedef struct LNode
{
    ElemType data; //结点的数据域
    struct LNode *next; //结点的指针域
} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型

//初始化一个空的循环单链表
bool InitList(LinkList &L);

//创建元素个数为N的循环单链表L,输入数据以空格分隔
void  CreateCircularList( LinkList &L,int N );

/*顺序输出链表每个结点,注意循环链表遍历结束的判断,每个结点元素值以逗号间隔, 元素内格式为(n m) ,以换行符结束。 */
void DispList(LinkList L);

/*
   约瑟夫环Josephus(循环单链表、无头结点)
	编号为1,2,…,n的n个人按顺时针方向围坐一圈,
	每人持有一个密码(正整数)。
	一开始任选一个正整数作为报数上限m,
	从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
	报m的人出列,将他的密码作为一个新的m值,
	从他在顺时针方向上的下一个人开始重新从1报数
	,如此下去,直至所有人全部出列为止。
	*/
void Josephus(LinkList &L,int M);

//创建元素个数为N的循环单链表L,输入数据以空格分隔
void  CreateCircularList( LinkList &L,int N )
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
int i,m;
LNode *p,*r;//r用来只想循环单链表的表尾
for(i=1;i<=N;i++)
{
    cin>>m;
    p=new LNode;
    p->data.m=m;
    p->data.n=i;
    if(L==NULL)//循环单链表为空
    {
        L=p;
        L->next=L;//只有一个元素,既是表头也是表尾
        r=L;
            }
            else//否则将p结点插入表尾,此时p的后继就是单链表的首结点即所指向结点,p编程新的表尾
            {
                p->next=L;
                r->next=p;
                r=p;
            }
}

    /********** End **********/  
    
}


/*
   约瑟夫环Josephus(循环单链表、无头结点)
	编号为1,2,…,n的n个人按顺时针方向围坐一圈,
	每人持有一个密码(正整数)。
	一开始任选一个正整数作为报数上限m,
	从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
	报m的人出列,将他的密码作为一个新的m值,
	从他在顺时针方向上的下一个人开始重新从1报数
	,如此下去,直至所有人全部出列为止。
	*/
void Josephus(LinkList &L,int M)
{
   // 请在这里补充代码,完成本关任务
    /********** Begin *********/
LNode *p=L,*pre;//指向当前节点
int i;
pre=L;
while(pre->next!=L){
    pre=pre->next;     //找到首结点的前驱结点

}
while(p)
{
    for(i=1;i<M;i++)
{
    pre=p;
    p=p->next;
}
cout<<" "<<p->data.n;
M=p->data.m;   //使用新的密码M作为下一轮的密码
if(p->next!=p)
{
    pre->next=p->next;// 将p结点从链表中删除
    delete p;
    p=pre->next;
}
else{
    delete p;
    p=NULL;
}
}
L=NULL;

    /********** End **********/  
   
}

 

<think>好的,用户想了解链表进阶的内容。首先,我需要回顾链表的基础知识,确保用户已经掌握了基本概念,比如节点结构、单链表、双链表等。然后,思考进阶内容可能包括哪些方面。常见的高级链表结构有循环链表、双向循环链表,还有应用场景如LRU缓存、跳表等。此外,算法方面如快慢指针、翻转链表、合并链表等也是重点。 接下来,要考虑用户可能的背景。可能是学生准备面试,或者开发者想深化数据结构知识。需要结构清晰,逐步展开。首先介绍高级链表类型,然后讲常见算法,接着是实际应用,最后提到优化和注意事项。这样用户能有一个系统的认识。 需要注意用户提到的格式要求,所有数学表达式和代码示例要正确使用LaTeX,行内用$...$,独立公式用$$...$$。比如,在解释时间复杂度时,需要用O(n)这样的格式。同时,回答要用中文,保持口语化,避免使用Markdown。 还要检查是否有遗漏的重要知识点,比如哨兵节点、内存管理问题,以及不同语言实现的差异。这些对用户深入理解链表很重要。最后,确保内容真实可靠,例子典型,比如翻转链表和检测环是面试常见题,LRU缓存是实际应用例子。 总结回答结构:先分类介绍进阶链表类型,再讲操作算法,接着应用场景,最后注意事项。中间穿插示例和复杂度分析,帮助用户逐步理解。确保每个部分简明扼要,重点突出,符合用户的学习需求。</think>链表进阶知识可以围绕以下方向展开,我们将逐步解析其核心概念和应用场景: 一、高级链表类型 1. 循环链表 $$ \text{尾结点指针} \rightarrow \text{头结点} $$ • 特点:实现环形数据结构应用:约瑟夫问题、轮询调度 2. 双向链表 $$ \text{结点结构:} [prev|data|next] $$ • 时间复杂度对比: - 插入/删除:$O(1)$(已知前驱) -链表则为$O(n)$ 3. 跳表(Skip List) • 空间换时间结构,平均$O(\log n)$的查询效率 • 实现原理:建立多级索引层 二、经典算法操作 1. 快慢指针 ```python # 检测环存在性 slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: return True ``` 2. 链表翻转(递归实现) $$ \text{递归公式:} reverse(head) = reverse(head.next) + \text{连接操作} $$ 3. 合并有序链表 • 关键点:虚拟头结点(dummy node)的使用 三、典型应用场景 1. LRU缓存淘汰算法 • 使用双向链表+哈希表实现$O(1)$操作 • 最近访问的节点移动到链表头部 2. 多项式表示 • 每个结点存储$ax^n$项 • 实现多项式加法的时间复杂度$O(m+n)$ 四、优化与注意事项 1. 哨兵节点技巧 • 减少边界条件判断 • 示例代码: ```python dummy = ListNode(0) dummy.next = head ``` 2. 内存管理 • C/C++需注意内存释放 • Java/Python依赖GC但要注意对象引用 3. 工程实践差异 • Linux内核链表实现:侵入式设计 • Redis列表:双向链表+压缩列表优化 理解这些进阶内容后,建议通过LeetCode典型题目巩固: - 25. K个一组翻转链表 - 146. LRU缓存机制 - 141/142. 环形链表系列 - 23. 合并K个升序链表 实际开发中,链表常用于需要频繁插入删除的场景,但要注意其缓存不友好的特性,在性能敏感场景可考虑数组或混合结构替代方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小狗碎碎念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值