链表操作:分区与回文判断

目录

链表分区(Partition)

功能概述

代码实现

要点与难点

注意事项

链表回文判断(PalindromeList)

功能概述

代码实现

要点与难点

注意事项

总结


在链表相关的算法问题中,理解链表的基本结构和操作至关重要。今天我们深入探讨两个经典的链表问题:链表分区和链表回文判断,通过详细分析代码实现,理解其中的要点、难点和注意事项。

作者主页:共享家9527-CSDN博客

链表分区(Partition)

功能概述

链表分区的目标是将给定链表按照某个值 x 进行划分,使得所有小于 x 的节点排在大于或等于 x 的节点之前。

代码实现

cpp 复制代码
cpp

class Partition {

public:

    ListNode* partition(ListNode* pHead, int x) {

        // 定义用于存储大于等于x节点链表的头指针和尾指针,以及小于x节点链表的头指针和尾指针,还有遍历原链表的指针

        struct ListNode* greatHead, *greattail, *lessHead, *lesstail, *cur; 

        // 为大于等于x节点链表的头指针和尾指针分配内存,并初始化为同一节点

        greatHead = greattail = (struct ListNode*)malloc(sizeof(struct ListNode)); 

        // 为小于x节点链表的头指针和尾指针分配内存,并初始化为同一节点

        lessHead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode)); 

        // 将遍历指针指向原链表头节点

        cur = pHead;  



        // 遍历原链表进行尾插

        while(cur)

        {

            if(cur->val < x)

            {

                // 将当前小于x的节点接到less链表的尾部

                lesstail->next = cur;  

                // 更新less链表的尾指针为当前节点

                lesstail = cur;   

                // 原链表中cur指针继续指向下一个节点

                cur = cur->next;  

            }

            else

            {

                // 将当前大于等于x的节点接到great链表的尾部

                greattail->next = cur; 

                // 更新great链表的尾指针为当前节点

                greattail = cur;  

                // 原链表中cur指针继续指向下一个节点

                cur = cur->next;  

            }

        }



        // 将less链表和great链表连接起来

        lesstail->next = greatHead->next;  

        // 将great链表的最后一个节点的next指针置为NULL,避免原链表的链接干扰

        greattail->next = NULL;           



        // 返回less链表的第一个有效节点(去掉虚拟头节点)

        return lessHead->next; 

    }

};

要点与难点

**1. 虚拟头节点的使用:**为简化链表操作,创建了两个虚拟头节点 lessHead 和 greatHead ,分别用于存储小于 x 和大于等于 x 的节点。这避免了对链表头节点的特殊处理,让代码更简洁统一。

2. 尾插法:通过 lesstail 和 greattail 指针,采用尾插法将原链表中的节点分别插入到两个新链表中,保持节点的相对顺序。

**3. 链表连接:**遍历结束后,将 lesstail 的 next 指针指向 greatHead->next ,实现两个链表的连接。

**4. 最后节点处理:**必须将 greattail 的 next 指针设为 NULL ,否则可能会形成环或导致错误的链表结构。

注意事项

**- 内存分配:**使用 malloc 分配内存时,记得在不再使用时释放内存,避免内存泄漏。

**- 边界条件:**考虑链表为空或只有一个节点的情况,确保代码的鲁棒性。

链表回文判断(PalindromeList)

功能概述

判断给定的链表是否为回文链表,即正向和反向读取链表节点的值是相同的。

代码实现

cpp 复制代码
cpp

class PalindromeList {

  public:

    bool chkPalindrome(ListNode* A) {

        // 定义慢指针,初始指向链表头节点

        struct ListNode* slow = A; 

        // 定义快指针,初始指向链表头节点

        struct ListNode* fast = A; 

        // 利用快慢指针找链表中间节点,快指针每次走两步,慢指针每次走一步

        while (fast->next) { 

            slow = slow->next;

            fast = fast->next;

            // 如果快指针还有下一个节点,再走一步

            if (fast->next) { 

                fast = fast->next;

            }

        }

        // 用于存储反转后半部分链表的新头指针

        struct ListNode* change = NULL; 

        // 从中间节点开始处理后半部分链表

        struct ListNode* head = slow; 

        // 反转链表的后半部分

        while (head) {

            // 保存当前节点的下一个节点

            struct ListNode* next = head->next; 

            // 将当前节点指向前一个节点,实现反转

            head->next = change; 

            // 更新新的头指针为当前节点

            change = head; 

            // 继续处理下一个节点

            head = next; 

        }

        // 比较链表的前半部分和反转后的后半部分

        while (slow) {

            // 如果对应节点值不相等,返回false

            if (A->val != change->val) { 

                return false;

            }

            // 前半部分链表指针后移

            A = A->next; 

            // 反转后的后半部分链表指针后移

            change = change->next; 

        }

        // 所有对应节点值都相等,返回true

        return true; 

    }

};

要点与难点

**1. 快慢指针:**利用快慢指针找到链表的中间节点。快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针正好指向中间节点。这是找到链表中间位置的高效方法。

**2. 链表反转:**将链表的后半部分进行反转,通过改变指针方向实现。在反转过程中,需要小心处理指针的指向,确保链表结构不被破坏。

**3. 比较判断:**从链表的两端开始比较节点的值,若所有值都相同,则链表为回文链表。

注意事项

**- 指针操作:**在链表反转过程中,要注意保存当前节点的下一个节点,以免丢失链表结构。

**- 边界条件:**考虑链表长度为奇数和偶数的情况,确保代码在各种情况下都能正确判断。比如链表长度为奇数时,中间节点单独处理,不参与比较;链表长度为偶数时,中间两个节点分别属于前后两部分参与比较 。

总结

链表操作需要对指针有深入的理解和熟练的运用。无论是链表分区还是回文判断,关键在于合理利用指针来遍历、修改链表结构。在实现过程中,要注意边界条件的处理、内存管理以及代码的可读性和可维护性。通过不断练习和总结,我们可以更好地掌握链表相关的算法问题,提高编程能力。

相关推荐
love530love4 分钟前
巧用符号链接搬移C盘中的软件数据目录到其他盘
运维·开发语言·windows·docker
Adellle13 分钟前
Java基础面试知识路线
java·开发语言·面试
阑梦清川15 分钟前
蓝桥杯关于栈这个数据结构的一个算法题目
数据结构·算法·蓝桥杯
techpupil19 分钟前
LeetCode146.LRU 缓存(哈希表+双向链表)
链表·缓存·散列表
乔冠宇26 分钟前
leetcode热题100道——两数之和
java·javascript·算法·leetcode·职场和发展
Dream it possible!30 分钟前
LeetCode 热题 100_跳跃游戏 II(79_45_中等_C++)(贪心算法)
c++·算法·leetcode·贪心算法
Wukong.Sun1 小时前
JAVA多线程中的单例模式
java·开发语言
happy_lhz1 小时前
让“树和二叉树”埋在记忆土壤中--性质和概念
数据结构
JosieBook1 小时前
【C#语言】C#中的同步与异步编程:原理、示例与最佳实践
开发语言·c#·同步异步
努力努力再努力wz2 小时前
【Linux内核系列】:动静态库详解
linux·运维·服务器·c语言·开发语言