【C算法】链表算法

简介: 【C算法】链表算法

移除链表元素

链接: 移除链表元素

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) 
{
   //设置俩个指针
   //prev用来记录val前面的指针
   //假设将prev的指向head的前面,即为NULL
   struct ListNode* prev = NULL;
   //cur用来判断,cur指向链表第一个
   struct ListNode* cur = head;
   //利用prev和cur进行循环
   //当cur为NULL停止
   while(cur != NULL)
    {
      
        if(cur->val != val)
        {
            //cur的值不等于val时
            //prev向后移动一步
            //cur向后移动一步
            prev = cur;
            cur = cur->next;
        }
        else
        {
            //判断如果一个值为val时,此时prev任然为NULL;
            //需要将这种cur向后移
            if(prev == NULL)
            {
                //将head指向下一个
                head = cur->next;
                //释放cur
                free(cur);
                //cur重新指向head的值
                cur = head;
            }
            else
            {
                //cur的值等于val时
                //将prev指向的地址向后移动一步
                prev->next = cur->next;
                //释放cur所在的内存
                free(cur);
                //将粗人指向新的prev后面的位置
                cur = prev->next;
            }
              
        }
        
    }
    return head;
}

链表的中间结点

链接: 链表的中间结点

思路:使用快慢指针进行解答

1.定义一个快指针和慢指针均指向头指针

2.快指针一次走俩步,慢指针一次走一步

3.当快指针为空指针或者快指针的下一个指针为空时结束

代码详解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) 
{
    //定义一个快指针,一次走俩格
    struct ListNode* fast = head;
    //定义一个慢指针,一次走一格
    struct ListNode* slow = head;
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

返回倒数第k个结点

链接: 返回倒数第k个结点

思路:使用先后指针解答

1.定义一个先走指针fast和慢走指针slow

2.先走指针要比慢走指针快k步

3.然后俩个指针同时进行移动

4.当先走指针遇到NULL时停止

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int kthToLast(struct ListNode* head, int k)
{
    //定义一个先走指针,
    struct ListNode* fast = head;
    //定义一个慢走指针
    struct ListNode* slow = head;
    //先走指针比慢走指针快k步
    while(k--)
    {
        fast = fast->next;
    }
    //俩个指针一起走
    while(fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow->val;
}

合并俩个有序链表

链接:合并俩个有序链表

思路:

1.定义cur1指向list1的头结点,定义cur2指向list2的头结点,定义head和end指向空

2.cur1用来遍历list1,cur2用来遍历list2,head用来记录合并后的头结点,end用来记录合并后的尾结点,以确保可以向后添加结点

3.判断是否有空,但凡有一个为空,就可以返回另外一个;如果俩个都不为空,需要设置合并后的头结点head

4.当俩个都不为空,进入循环,向end后面接结点

5.当俩个中间有一个为空时,可以只在将另一个接在end后面

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
  //设置四个结点
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    struct ListNode* head = NULL;
    struct ListNode* end = NULL;
    //判断如何都不为空
    if (cur1 && cur2)
    {
    //将合并后头结点head指向小值
        if (cur1->val < cur2->val)
        {
            head = end = cur1;
            cur1 = cur1->next;
        }
        else
        {
            head = end = cur2;
            cur2 = cur2->next;
        }
    }
    //cur1与cur2都不为空所以不需要判断else,如果没有else,设置完头结点head后,有一个可能为空,导致返回。
    else if (cur1 == NULL)//cur1为空,返回cur2
    {
        return cur2;
    }
    else if (cur2 == NULL)//cur2为空。返回cur1
    {
        return cur1;
    }
    //俩个都不为空进行插入链表
    while (cur1 && cur2)
    {
    //每次插入都需要,将cur和end向后移动
        if (cur1->val <= cur2->val)
        {
            end->next = cur1;
            cur1 = cur1->next;
            end = end->next;
        }
        else
        {
            end->next = cur2;
            cur2 = cur2->next;
            end = end->next;
        }
    }
    //如果其中一个为空,可以直接将另一个后面的全部接在end后面
    if (cur1 == NULL)
    {
        end->next = cur2;
    }
    if (cur2 == NULL)
    {
        end->next = cur1;
    }
    return head;
}

链表分割

链接:链表分割

思路:

1.首先需要创建俩个哨兵结点来将大值结点与小结点分割开

2.使用四个指针指向俩个哨兵结点,begin1、begin2、end1、end2

3.begin1与begin2保留俩个哨兵结点的位置,end1与end2接后面的结点

4.然后将俩个哨兵结点相连,注意需要释放空间

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) 
    {
        //创建俩个哨兵结点
        //使用begin1与end1指向第一个哨兵
        //使用begin2与end2指向第二个哨兵
        struct ListNode* begin1;
        struct ListNode* begin2;
        struct ListNode* end1;
        struct ListNode* end2;
        begin1 = (struct ListNode*)malloc(sizeof(struct ListNode));
        begin2 = (struct ListNode*)malloc(sizeof(struct ListNode));
        if(begin1 == NULL || begin2 == NULL)
        {
            perror("malloc fail");
            return NULL;
        }
        begin1->next = NULL;
        begin2->next = NULL;
        end1 = begin1;
        end2 = begin2;
        //将小于x的结点接在begin1后面
        //将大于等于x的结点接在begin2后面
        while(pHead != NULL)
        {
            if(pHead->val < x)
            {
                end1->next = pHead;
                end1 = end1->next;
            }
            else
            {
                end2->next = pHead;
                end2 = end2->next;
            }
            pHead = pHead->next;
        }
        //将end1与end2后面置空
        end1->next = NULL;
        end2->next = NULL;
        //将大于等于x的结点接在小于x的结点后面
        end1->next = begin2->next;
        //将小于x的第一个结点设置为头结点
        pHead = begin1->next;
        //注意需要释放malloc创建的堆区已经野指针问题
        free(begin1);
        free(begin2);
        begin1 = begin2 = end1 = end2 = NULL;
        return pHead;
    }
};

相交链表

链接: 相交链表

思路:

1.定义俩个指向链表的指针和俩个计算结点个数(长度)的变量

2.求出俩个链表有几个结点,即链表的长度

3.如果最后一个结点处,二者并没有相交,则返回NULL

4.将俩个链表的结点个数(长度)相减,让结点个数(长度)多(长)的先走相减的个数。

5.最后俩条链表同时走,遇到相同结点时结束

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //设置L1和L2计算俩条链的长度
    int L1 = 1, L2 = 1;
    //设置俩个指针指向俩条链的头结点
    struct ListNode* cur1 = headA;
    struct ListNode* cur2 = headB;
    //计算L1的长度
    while(cur1->next != NULL)
    {
        cur1 = cur1->next;
        ++L1;
    }
    //计算L2的长度
    while(cur2->next != NULL)
    {
        cur2 = cur2->next;
        ++L2;
    }
    //判断如果俩个直接的最好交点不在一起则直接返回NULL
    if(cur1 != cur2)
    {
        return NULL;
    }
    //计算链的长度差
    int L = L1 - L2;
    //将移动的指针重新指向头结点
    cur1 = headA;
    cur2 = headB;
    //让长度较长的指针先走L步
    if(L > 0)
    {
        while(L--)
        {
            cur1 = cur1->next;
        }
    }
    else
    {
        while(L++)
        {
            cur2 = cur2->next;
        }
    }
    //俩个指针同时走,发现相同时停止
    while(cur1 != cur2)
    {
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return cur1;
}

环形链表

链接: 环形链表

思路:

1.判断是否存在空结点和只有一个结点的情况

2.定义一个快结点和一个慢结点

3.快结点一次走俩步,慢结点一次走一步

4.当二者相遇时结束

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) 
{
    //判断是否存在空链表或者链表中只存在一个结点
    if(head == NULL || head->next == NULL)
    {
        return false;
    }
    //定义一个快结点和慢结点
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //快结点一次走俩步
    //慢结点一次走一步
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        //相遇时结束
        if(fast == slow)
        {
            return true;
        }
    }
    return false;
}

链表的回文结构

链接: 链表的回文结构

思路:

1.判断当存在空链表或者当个链表时返回true

2.设置快慢指针求得中间结点

3.使用三个指针翻转后半段链表

4.使用一前一后俩个指针判断是否为回文结构

难点:使用三个指针翻转链表时,循环停止的条件为中间的指针为空,此时需要第三个结点在循环内初始化,这样才能保证后面每一个链表都能翻转过来,并且当第三个结点为空时,不会执行next

  • 代码实现:
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) 
    {
        //链表为空或者链表只有一个返回true
        if(A == NULL || A->next == NULL)
        {
            return true;
        }
        //设置快慢指针
        //快指针一次走俩步
        //慢指针一次走一步
        struct ListNode* fast = A;
        struct ListNode* slow = A;
        //寻找中间结点
        while(fast != NULL && fast->next != NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //slow此时所在位置为中间结点
        //从中间结点往后开始翻转链表
        struct ListNode* cur = slow->next;
        while(cur != NULL)
        {
            struct ListNode* cur_next = cur->next;
            cur->next = slow;
            slow = cur;
            cur = cur_next;
        }
        //设置一个头结点和尾结点
        struct ListNode* tail = slow;
        struct ListNode* head = A;
        //一前一后开始比较
        //比较正确,比较下一个
        //比较错误,返回错误
        while( head != tail)//奇数时二者相遇
        {
            if(head->val != tail->val)
            {
                return false;
            }
            if(head->next == tail)
            {
                //偶数时head的下一个是tail
                return true;
            }
            head = head->next;
            tail = tail->next;
        }
        return true;
    }
};


相关文章
|
10天前
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
39 1
|
10天前
|
算法 索引
❤️算法笔记❤️-(每日一刷-141、环形链表)
❤️算法笔记❤️-(每日一刷-141、环形链表)
23 0
|
10天前
|
算法
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
29 0
|
10天前
|
算法
【❤️算法笔记❤️】-每日一刷-23、合并 K 个升序链表
【❤️算法笔记❤️】-每日一刷-23、合并 K 个升序链表
24 0
|
10天前
|
存储 算法
【❤️算法笔记❤️】-每日一刷-21、合并两个有序链表
【❤️算法笔记❤️】-每日一刷-21、合并两个有序链表
37 0
|
9天前
|
存储 缓存 算法
经典算法之链表篇(三)
经典算法之链表篇(三)
|
9天前
|
算法
经典算法之链表篇(二)
经典算法之链表篇(二)
|
9天前
|
算法 索引
经典算法之链表篇
经典算法之链表篇
|
10天前
|
算法
❤️算法笔记❤️-(每日一刷-160、相交链表)
❤️算法笔记❤️-(每日一刷-160、相交链表)
12 1
|
11天前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
10 0
数据结构与算法学习五:双链表的增、删、改、查

热门文章

最新文章