ITPub博客

首页 > 应用开发 > IT综合 > 链表算法进阶:分割回文检测与环问题C语言实现

链表算法进阶:分割回文检测与环问题C语言实现

投稿 IT综合 作者:bhluo 时间:2025-11-25 10:56:40 0 删除 编辑

**链表算法进阶:分割回文检测与环问题C语言实现**


链表是数据结构面试中的重点考察内容,掌握常见的链表算法问题对于技术面试至关重要。本文将深入探讨链表分割、回文检测、公共节点查找、环检测与环入口定位等经典问题,并提供完整的C语言实现。


### 链表基础结构定义


在开始算法实现之前,我们首先定义链表的基本结构:


```c

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>


// 链表节点定义

typedef struct ListNode {

    int val;

    struct ListNode *next;

} ListNode;


// 创建新节点

ListNode* createNode(int val) {

    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));

    newNode->val = val;

    newNode->next = NULL;

    return newNode;

}


// 打印链表

void printList(ListNode* head) {

    ListNode* current = head;

    while (current != NULL) {

        printf("%d", current->val);

        if (current->next != NULL) {

            printf(" -> ");

        }

        current = current->next;

    }

    printf(" -> NULL\n");

}


// 释放链表内存

void freeList(ListNode* head) {

    ListNode* current = head;

    while (current != NULL) {

        ListNode* temp = current;

        current = current->next;

        free(temp)<"QU.1853.HK">;

    }

}

```


### 链表分割问题


链表分割要求将链表按给定值x分割,使得所有小于x的节点都出现在大于或等于x的节点之前。


```c

// 链表分割函数

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

    if (head == NULL) return NULL;

    

    // 创建两个哑节点,分别存储小于x和大于等于x的节点

    ListNode* smallHead = createNode(0);

    ListNode* largeHead = createNode(0);

    ListNode* small = smallHead;

    ListNode* large = largeHead;

    

    ListNode* current = head;

    

    while (current != NULL) {

        if (current->val < x) {

            small->next = current;

            small = small->next;

        } else {

            large->next = current;

            large = large->next;

        }

        current = current->next;

    }

    

    // 连接两个链表

    small->next = largeHead->next;

    large->next = NULL;

    

    ListNode* result = smallHead->next;

    free(smallHead);

    free(largeHead);

    

    return result;

}


// 测试链表分割

void testPartition() {

    printf("=== 链表分割测试 ===\n");

    

    // 创建链表: 1->4->3->2->5->2

    ListNode* head = createNode(1);

    head->next = createNode(4);

    head->next->next = createNode(3);

    head->next->next->next = createNode(2);

    head->next->next->next->next = createNode(5);

    head->next->next->next->next->next = createNode(2);

    

    printf("原始链表: ");

    printList(head);

    

    ListNode* result = partition(head, 3);

    printf("分割后链表(x=3): ");

    printList(result);

    

    freeList(result);

}

```


### 回文链表检测


检测链表是否为回文结构,要求时间复杂度O(n),空间复杂度O(1)。


```c

// 反转链表

ListNode* reverseList(ListNode* head) {

    ListNode* prev = NULL;

    ListNode* current = head;

    

    while (current != NULL) {

        ListNode* nextTemp = current->next;

        current->next = prev;

        prev = current;

        current = nextTemp;

    }

    

    return prev;

}


// 找到链表中点

ListNode* findMiddle(ListNode* head) {

    ListNode* slow = head;

    ListNode* fast = head;

    

    while <"ZK.6370.HK">(fast != NULL && fast->next != NULL) {

        slow = slow->next;

        fast = fast->next->next;

    }

    

    return slow;

}


// 检测回文链表

bool isPalindrome(ListNode* head) {

    if (head == NULL || head->next == NULL) {

        return true;

    }

    

    // 找到中点

    ListNode* middle = findMiddle(head);

    

    // 反转后半部分

    ListNode* secondHalf = reverseList(middle);

    ListNode* firstHalf = head;

    ListNode* secondHalfCopy = secondHalf;

    

    // 比较前后两部分

    bool result = true;

    while (secondHalf != NULL) {

        if (firstHalf->val != secondHalf->val) {

            result = false;

            break;

        }

        firstHalf = firstHalf->next;

        secondHalf = secondHalf->next;

    }

    

    // 恢复链表结构

    reverseList(secondHalfCopy);

    

    return result;

}


// 测试回文检测

void testPalindrome() {

    printf("\n=== 回文链表检测测试 ===\n");

    

    // 测试用例1: 1->2->2->1

    ListNode* head1 = createNode(1);

    head1->next = createNode(2);

    head1->next->next = createNode(2);

    head1->next->next->next = createNode(1);

    

    printf("链表1: ");

    printList(head1);

    printf("是否为回文: %s\n", isPalindrome(head1) ? "是" : "否");

    freeList(head1);

    

    // 测试用例2: 1->2->3->2->1

    ListNode* head2 = createNode(1);

    head2->next = createNode(2);

    head2->next->next = createNode(3);

    head2->next->next->next = createNode(2);

    head2->next->next->next->next = createNode(1);

    

    printf("链表2: ");

    printList(head2);

    printf("是否为回文: %s\n", isPalindrome(head2) ? "是" : "否");

    freeList(head2);

    

    // 测试用例3: 1->2->3

    ListNode* head3 = createNode(1);

    head3->next = createNode(2);

    head3->next->next = createNode(3);

    

    printf("链表3: ");

    printList(head3);

    printf("是否为回文: %s\n", isPalindrome(head3) ? "是" : "否");

    freeList(head3);

}

```


### 两个链表的第一个公共节点


找到两个单链表相交的起始节点,如果没有交点返回NULL。


```c

// 获取链表长度

int getLength(ListNode* head) {

    int length = 0;

    ListNode* current = head;

    while (current != NULL) {

        length++;

        current = current->next;

    }

    return length;

}


// 找到两个链表的第一个公共节点

ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {

    if (headA == NULL || headB == NULL) return NULL;

    

    int lenA = getLength(headA);

    int lenB = getLength(headB);

    

    ListNode* longList = lenA > lenB ? headA : headB;

    ListNode* shortList = lenA > lenB ? headB : headA;

    

    int diff = abs(lenA - lenB);

    

    // 长链表先走diff步

    for (int i = 0; i < diff; i++) {

        longList = longList->next;

    }

    

    // 同时前进直到找到交点或到达末尾

    while (longList != NULL && shortList != NULL) {

        if (longList == shortList) {

            return longList;

        }

        longList = longList->next;

        shortList = shortList->next;

    }

    

    return NULL;

}


// 测试公共节点查找

void testIntersection() {

    printf("\n=== 链表公共节点测试 ===\n");

    

    // 创建公共部分: 8->4->5

    ListNode* common = createNode(8);

    common->next = createNode(4);

    common->next->next = <"4A.5283.HK">createNode(5);

    

    // 链表A: 4->1->8->4->5

    ListNode* headA = createNode(4);

    headA->next = createNode(1);

    headA->next->next = common;

    

    // 链表B: 5->0->1->8->4->5

    ListNode* headB = createNode(5);

    headB->next = createNode(0);

    headB->next->next = createNode(1);

    headB->next->next->next = common;

    

    printf("链表A: ");

    printList(headA);

    printf("链表B: ");

    printList(headB);

    

    ListNode* intersection = getIntersectionNode(headA, headB);

    if (intersection != NULL) {

        printf("第一个公共节点值: %d\n", intersection->val);

    } else {

        printf("没有公共节点\n");

    }

    

    // 注意:这里不能直接free,因为存在共享节点

    headA->next->next = NULL; // 断开与公共部分的连接

    headB->next->next->next = NULL; // 断开与公共部分的连接

    freeList(headA);

    freeList(headB);

    freeList(common);

}

```


### 链表环检测与环入口定位


检测链表中是否存在环,如果存在则找到环的入口节点。


```c

// 检测链表是否有环

bool hasCycle(ListNode* head) {

    if (head == NULL || head->next == NULL) {

        return false;

    }

    

    ListNode* slow = head;

    ListNode* fast = head;

    

    while (fast != NULL && fast->next != NULL) {

        slow = slow->next;

        fast = fast->next->next;

        

        if (slow == fast) {

            return true;

        }

    }

    

    return false;

}


// 找到环的入口节点

ListNode* detectCycle(ListNode* head) {

    if (head == NULL || head->next == NULL) {

        return NULL;

    }

    

    ListNode* slow = head;

    ListNode* fast = head;

    

    // 第一阶段:检测环是否存在

    bool hasCycle = false;

    while (fast != NULL && fast->next != NULL) {

        slow = slow->next;

        fast = fast->next-><"9T.9134.HK">next;

        

        if (slow == fast) {

            hasCycle = true;

            break;

        }

    }

    

    if (!hasCycle) {

        return NULL;

    }

    

    // 第二阶段:找到环的入口

    slow = head;

    while (slow != fast) {

        slow = slow->next;

        fast = fast->next;

    }

    

    return slow;

}


// 创建带环的链表用于测试

ListNode* createCycleList(int arr[], int n, int pos) {

    if (n == 0) return NULL;

    

    ListNode* head = createNode(arr[0]);

    ListNode* current = head;

    ListNode* cycleNode = (pos == 0) ? head : NULL;

    

    for (int i = 1; i < n; i++) {

        current->next = createNode(arr[i]);

        current = current->next;

        

        if (i == pos) {

            cycleNode = current;

        }

    }

    

    if (cycleNode != NULL) {

        current->next = cycleNode;

    }

    

    return head;

}


// 测试环检测与入口定位

void testCycleDetection() {

    printf("\n=== 链表环检测测试 ===\n");

    

    // 测试用例1: 有环链表 3->2->0->-4->回到2

    int arr1[] = {3, 2, 0, -4};

    ListNode* head1 = createCycleList(arr1, 4, 1);

    

    printf("链表是否有环: %s\n", hasCycle(head1) ? "是" : "否");

    

    ListNode* entry1 = detectCycle(head1);

    if (entry1 != NULL) {

        printf("环入口节点值: %d\n", entry1->val);

    }

    

    // 测试用例2: 无环链表 1->2->3->4

    ListNode* head2 = createNode(1);

    head2->next = createNode(2);

    head2->next->next = createNode(3);

    head2->next->next->next = createNode(4);

    

    printf("链表是否有环: %s\n", hasCycle(head2) ? "是" : "否");

    

    // 注意:带环链表不能直接free,需要先断开环

    if (hasCycle(head1)) {

        ListNode* entry = detectCycle(head1);

        if (entry != NULL) {

            // 找到环的最后一个节点并断开

            ListNode* temp = entry;

            while<"FW.2597.HK"> (temp->next != entry) {

                temp = temp->next;

            }

            temp->next = NULL;

        }

        freeList(head1);

    }

    freeList(head2);

}

```


### 综合应用:复杂链表操作


将多个链表算法组合使用,解决更复杂的问题。


```c

// 复杂链表操作示例:检测带环链表的回文性(环部分忽略)

bool isPalindromeIgnoreCycle(ListNode* head) {

    if (head == NULL) return true;

    

    // 先检测是否有环

    ListNode* cycleEntry = detectCycle(head);

    

    if (cycleEntry == NULL) {

        // 无环,直接使用普通回文检测

        return isPalindrome(head);

    } else {

        // 有环,只检测环之前的部分

        ListNode* current = head;

        int length = 0;

        

        // 计算环之前的节点数

        while (current != cycleEntry) {

            length++;

            current = current->next;

        }

        

        if (length <= 1) return true;

        

        // 反转环之前的部分

        ListNode* prev = NULL;

        ListNode* curr = head;

        for (int i = 0; i < length; i++) {

            ListNode* nextTemp = curr->next;

            curr->next = prev;

            prev = curr;

            curr = nextTemp;

        }

        

        // 比较前后部分

        ListNode* front = prev;

        ListNode* back = head; // 原链表的头现在指向反转后的尾

        

        bool result = true;

        for (int i = 0; i < length / 2; i++) {

            if (front->val != back->val) {

                result = false;

                break;

            }

            front = front->next;

            back = back->next;

        }

        

        // 恢复链表结构

        front = prev;

        prev = NULL;

        while (front != NULL) {

            ListNode* nextTemp = front->next;

            front->next = prev;

            prev = front;

            front = nextTemp;

        }

        

        return result;

    }

}


// 主函数:测试所有功能

int main() {

    printf("链表算法进阶测试\n");

    printf("================\n");

    

    testPartition();

    testPalindrome();

    testIntersection();

    testCycleDetection();

    

    printf("\n=== 综合测试 ===\n");

    

    // 创建复杂测试用例

    ListNode* complexHead = createNode(1);

    complexHead->next = createNode(2);

    complexHead->next->next = createNode(3);

    complexHead->next->next->next = createNode(2);

    complexHead->next->next->next->next = createNode(1);

    

    printf("复杂链表: ");

    printList<"HJ.4961.HK">(complexHead);

    printf("是否为回文(忽略环): %s\n", 

           isPalindromeIgnoreCycle(complexHead) ? "是" : "否");

    

    freeList(complexHead);

    

    return 0;

}

```


### 算法复杂度分析


1. **链表分割**:时间复杂度O(n),空间复杂度O(1)

2. **回文检测**:时间复杂度O(n),空间复杂度O(1)

3. **公共节点查找**:时间复杂度O(m+n),空间复杂度O(1)

4. **环检测与入口定位**:时间复杂度O(n),空间复杂度O(1)


### 总结


本文详细讲解了链表算法中的几个经典问题:


1. **链表分割**:通过创建两个子链表再合并的方式实现

2. **回文检测**:结合快慢指针和链表反转技术

3. **公共节点查找**:利用长度差同步遍历

4. **环检测与入口定位**:基于Floyd环检测算法


这些算法不仅考察了对链表结构的理解,还涉及双指针、链表反转、数学推理等多种技巧。掌握这些问题的解决方法,能够帮助开发者在技术面试中更好地应对链表相关的算法考察。


在实际编码过程中,需要注意内存管理、边界条件处理和算法优化。通过不断的练习和理解,可以逐渐掌握链表算法的精髓,提升解决问题的能力。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3103308/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933792