数据结构(C\C++)——单链表OJ

前言

本文介绍几道典型算法题的思路
移除链表元素、反转链表、链表的中间结点、合并两个有序链表、链表分割、链表的回文结构、相交链表以及两种环形链表

单链表算法题

    • 前言
    • 移除链表元素
    • 反转链表
    • 链表的中间结点
    • 合并两个有序链表
    • 链表分割
    • 链表的回文结构
    • 相交链表
    • 环形链表I
    • 环形链表II
    • 更多链表算法刷题入口:

移除链表元素

  • 移除链表元素

思路:以空间换时间,将值不为val的结点拿下来尾插到newhead

typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
    // 创建新链表
    ListNode *newhead = NULL, *newtail = NULL;

    ListNode* pcur = head;
    while (pcur) {
        // 把值不为val的结点插到新的链表中去
        if (pcur->val != val) {
            // 尾插
            // 链表为空
            if (newhead == NULL) {
                newhead = newtail = pcur;
            } else 
            {
                newtail->next = pcur;
                newtail = newtail->next;
            }
        }
            pcur = pcur->next;
        
    }
    //pcur为空
    if(newtail)
        newtail->next = NULL;
    return newhead;
}

反转链表

  • 反转链表

思路:三指针法转向,n1转向,n2,n3后移;

原来
旋转后

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
        if(head == NULL)
        {
            return head;
        }
        ListNode*n1,*n2,*n3;
        n1 = NULL;
        n2 = head;
        n3 = n2->next;
        while(n2)
        {
            n2->next = n1;
            n1 = n2;
            n2 = n3;
            if(n3)
            {
                n3 = n3->next;
            } 
        }
    
    return n1;
}

链表的中间结点

  • 链表的中间结点

思路:快慢指针,慢指针走一步,快指针走两步

 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) 
{
    ListNode* show = head;
    ListNode* fast = head;

    while(fast&&fast->next)
    {
        show = show->next;
        fast = fast->next->next;
    }
    return show;
}

合并两个有序链表

  • 合并两个有序链表

思路:建立新的链表,比较小的尾插进去,遍历结束后,如果哪个不为空,将剩余的全部尾插进去

 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if(list1 == NULL)
        return list2;
    if(list2 == NULL)
    {
        return list1;
    }
    ListNode* newhead ,*newtail;
    //创建非空链表
    newhead = newtail = (ListNode*)malloc(sizeof(ListNode));

    ListNode *l1 = list1;
    ListNode *l2 = list2;

    while(l1 && l2)
    {
        if(l1->val<l2->val)
        {
            newtail->next = l1;
            newtail = newtail->next;
            l1 = l1->next;
        }
        else{
            newtail->next = l2;
            newtail = newtail->next;
            l2 = l2->next;
        }

    }
    if(l1)
    {
        newtail->next = l1;
    }
    if(l2)
    {
        newtail ->next = l2;
    }

    ListNode*rehead = newhead->next;
    free(newhead);
    newhead = NULL;
    return rehead;
}

链表分割

  • 链表分割

思路:
创建两个子链表:

less链表:存储所有值小于x的节点。
great链表:存储所有值大于等于x的节点。 ,避免处理头节点为空的情况。
遍历原链表: 逐个检查每个节点,根据节点值将其添加到对应的子链表末尾。 连接子链表:
将less链表的末尾连接到great链表的头部。 确保great链表的末尾节点的next指针置为NULL,防止成环。
释放虚拟头节点并返回结果: 合并后的链表头为less虚拟头节点的下一个节点。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* phead, int x) {
        // write code here
        ListNode* greathead ,*greattail ;
        greathead = greattail= (ListNode*)malloc(sizeof(ListNode));
        ListNode* lesshead,*lesstail;
        lesshead =lesstail = (ListNode*)malloc(sizeof(ListNode));

        ListNode*pcur = phead;
        while(pcur)
        {
            if(pcur->val<x)
            {
                lesstail->next = pcur;
                lesstail = lesstail->next;
            }
            else {
                greattail->next = pcur;
                greattail = greattail->next;
            }
            pcur = pcur->next;
        }
        greattail->next = NULL;
        lesstail->next = greathead->next;
        ListNode* ret = lesshead->next;
        free(greathead);
        free(lesshead);
        return ret;
    }
};

链表的回文结构

  • 链表的回文结构

思路
找到中间结点
反转后半段
遍历链表,将前半段与后半段比较,若相同则为回文结构

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    ListNode* middle(ListNode*A)
    {
        ListNode* show,*fast;
        show = fast = A;

        while(fast&&fast->next)
        {
            show = show->next;
            fast = fast->next->next;
        }
        return show;
    }

    ListNode* reveselist(ListNode*A)
    {
        if(A==NULL)
            return A;
        ListNode* n1,*n2,*n3;
        n1 = NULL;
        n2 = A;
        n3 = A->next;

        while(n2)
        {
            n2->next = n1;
            n1 = n2;
            n2 = n3;
            if(n3)
            {
                n3 = n3->next;
            }
        }
        return n1;

    }
    bool chkPalindrome(ListNode* A) {
        // write code here
        //找中间结点
        ListNode * mid = middle(A);
        //反转后半段 
        ListNode* right = reveselist(mid);
        //head与mid比较
        ListNode* left = A;

        while(right)
        {
            if(left->val != right->val)
            {
                return false;
            }
            left = left->next;
            right = right->next;
        }
        return true;
    }
};

相交链表

  • 相交链表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
	//计算两个链表的长度
    ListNode* pa = headA;
    ListNode* pb = headB;

    int sizea = 0;
    int sizeb = 0;

    while(pa)
    {
        sizea++;
        pa = pa->next;
    }
    while(pb)
    {
        sizeb++;
        pb = pb->next;
    }
    
    //计算差值
    int gap = abs(sizea-sizeb);

    ListNode* longlist = headA;
    ListNode* shortlist = headB;

    if(sizea<sizeb)
    {
        longlist = headB;
        shortlist = headA;
    }
    //让长的一个先走gap步
    while(gap--)
    {
        longlist = longlist->next;
    }
    //同时遍历,若长的和短的相等,则相交
    while(shortlist)
    {
        if(longlist == shortlist)
        {
            return shortlist;
        }
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    return NULL;
}

环形链表I

  • 环形链表I:

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* fast = head;
    ListNode* show = head;

    while(fast&&fast->next)
    {
        show = show->next;
        fast = fast->next->next;
        if(fast == show)
            return true;
    }
    return false;
}

环形链表II

  • 环形链表II:

思路:让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
	//定义快慢指针
    ListNode* show = head;
    ListNode* fast = head;
	//遍历链表
    while(fast&&fast->next)
    {
        show = show->next;
        fast = fast->next->next;
		
        if(show == fast)
        {
        //找到相遇点
        //相遇点和头节点到入环第一个结点的距离相等
            ListNode* pcur = head;
            //此时fast和slow重合,下面哪个都可以
            while(pcur!=fast)
            {
               
                fast = fast->next;
                pcur = pcur->next;
            }
            //pcur == fast
            return pcur;
        }
    }
    return NULL;
}

更多链表算法刷题入口:

  • 牛客网: https://www.nowcoder.com/exam/oj
  • LeetCode: https://leetcode.cn/problems/copy-list-with-random-pointer/description/

你可能感兴趣的:(数据结构,数据结构,c语言,c++)