Bootstrap

【数据结构与算法】第4课—数据结构单链表OJ练习题

1. 移除链表元素

  • 题目

在这里插入图片描述


  • 思路

在这里插入图片描述


在这里插入图片描述


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//移除链表元素
typedef struct ListNode {
    int val;
    struct ListNode* next;
}NodeList;
 
//移除
 NodeList* removeElements(NodeList* head, int val)
{
    assert(head);
    NodeList* newhead = NULL; //新链表的头
    NodeList* newtail = NULL; //新链表的尾
    NodeList* pcur = head;

    //遍历链表
    while (pcur)
    {
        if (pcur->val != val)
        {
            //新链表为空
            if (newhead == NULL)
                newhead = newtail = pcur;
            //新链表不为空
            else
            {
                newtail->next = pcur;
                newtail = newtail->next;
            }
        }
        pcur = pcur->next;
    }
    if (newtail)
        newtail->next = NULL;
    return newhead;
}

//打印
void NodeListPrint(NodeList* phead)
{
    assert(phead);
    while (phead)
    {
        printf("%d -> ", phead->val);
        phead = phead->next;
    }
    printf("NULL\n");
}

void test()
{
    //初始化
    NodeList* node1 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node2 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node3 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node4 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node5 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node6 = (NodeList*)malloc(sizeof(NodeList));
    NodeList* node7 = (NodeList*)malloc(sizeof(NodeList));

    if (node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL || node5 == NULL || node6 == NULL || node7 == NULL)
    {
        perror("malloc");
        exit(1);
    }
    node1->val = 1;
    node2->val = 2;
    node3->val = 6;
    node4->val = 3;
    node5->val = 3;
    node6->val = 5;
    node7->val = 6;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    node6->next = node7;
    node7->next = NULL;

    printf("移除之前链表为>:");
    NodeListPrint(node1);
    NodeList* newnode = removeElements(node1, 6);
    printf("移除之后链表为>:");
    NodeListPrint(newnode);

}
int main()
{
    test();
    return 0;
}

在这里插入图片描述


2. 反转链表

  • 题目

在这里插入图片描述


  • 思路

在这里插入图片描述


在这里插入图片描述

  • 代码
//反转链表
typedef struct ListNode {
    int val;
    struct ListNode* next;
}ListNode;

//反转
ListNode* reverseList(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;
}

//打印
void ListNodePrint(ListNode* phead)
{
    assert(phead);
    while (phead)
    {
        printf("%d -> ", phead->val);
        phead = phead->next;
    }
    printf("NULL\n");
}

void test()
{
    //初始化
    ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node5 = (ListNode*)malloc(sizeof(ListNode));

    if (node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL || node5 == NULL)
    {
        perror("malloc");
        exit(1);
    }
    node1->val = 1;
    node2->val = 2;
    node3->val = 3;
    node4->val = 4;
    node5->val = 5;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = NULL;

    printf("反转链表之前为>:");
    ListNodePrint(node1);
    ListNode* newnode = reverseList(node1);
    printf("反转链表之后为>:");
    ListNodePrint(newnode);

}
int main()
{
    test();
    return 0;
}

3. 找链表中间节点

  • 题目

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


  • 代码
//练习题:找链表中间节点
typedef struct ListNode 
{
    int val;
    struct ListNode* next;
}ListNode;

//找中间节点
ListNode* middleNode(ListNode* head)
{
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

//打印
void ListNodePrint(ListNode* phead)
{
    assert(phead);
    while (phead)
    {
        printf("%d -> ", phead->val);
        phead = phead->next;
    }
    printf("NULL\n");
}

void test()
{
    //初始化
    ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node5 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node6 = (ListNode*)malloc(sizeof(ListNode));

    if (node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL || node5 == NULL || node6 == NULL)
    {
        perror("malloc");
        exit(1);
    }
    node1->val = 1;
    node2->val = 2;
    node3->val = 3;
    node4->val = 4;
    node5->val = 5;
    node6->val = 6;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;
    node5->next = node6;
    node6->next = NULL;

    printf("链表为>:");
    ListNodePrint(node1);
    ListNode* newnode = middleNode(node1);
    printf("链表中间节点为>: %d\n", newnode->val);
}
int main()
{
    test();
    return 0;
}

4. 合并两个有序的链表

  • 题目

在这里插入图片描述


  • 思路

在这里插入图片描述


在这里插入图片描述


  • 优化

在这里插入图片描述


在这里插入图片描述


  • 代码
//练习题:合并有序数组
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;

//合并
//ListNode* mergeTwoLists1(ListNode* list1, ListNode* list2) 
//{
//    if (list1 == NULL)
//        return list2;
//    if (list2 == NULL)
//        return list1;
//    ListNode* l1 = list1;
//    ListNode* l2 = list2;
//    ListNode* newHead, * newTail;
//    newHead = newTail = NULL;
//    while (l1 && l2)
//    {
//            if (l1->val < l2->val)
//            {
//                if (newHead == NULL)
//                    newHead = newTail = l1;
//                else
//                {
//                    newTail->next = l1;
//                    newTail = newTail->next;
//                }
//                l1 = l1->next;
//            } 
//            else
//            {
//                if (newHead == NULL)
//                    newHead = newTail = l2;
//                else
//                {
//                    newTail->next = l2;
//                    newTail = newTail->next;
//                }
//                l2 = l2->next;
//            }   
//    }
//    if (l1)
//        newTail->next = l1;
//    if (l2)
//        newTail->next = l2;
//    return newHead;
//}

//优化代码
ListNode* mergeTwoLists2(ListNode* list1, ListNode* list2)
{
    if (list1 == NULL)
        return list2;
    if (list2 == NULL)
        return list1;
    ListNode* l1 = list1;
    ListNode* l2 = list2;
    ListNode* newHead, * newTail;
    newHead = newTail = (ListNode*)malloc(sizeof(ListNode));
    //l1 和 l2都不为空
    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* retnode = newHead->next;
    free(newHead);
    newHead = NULL;
    return retnode;
}

//打印
void ListNodePrint(ListNode* phead)
{
    assert(phead);
    while (phead)
    {
        printf("%d -> ", phead->val);
        phead = phead->next;
    }
    printf("NULL\n");
}

void test()
{
    //初始化
    ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node5 = (ListNode*)malloc(sizeof(ListNode));
    ListNode* node6 = (ListNode*)malloc(sizeof(ListNode));

    if (node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL || node5 == NULL || node6 == NULL)
    {
        perror("malloc");
        exit(1);
    }
    node1->val = 1;
    node2->val = 2;
    node3->val = 4;
    node4->val = 1;
    node5->val = 3;
    node6->val = 4;

    node1->next = node2;
    node2->next = node3;
    node3->next = NULL;
    node4->next = node5;
    node5->next = node6;
    node6->next = NULL;

    printf("合并之前链表1为>:");
    ListNodePrint(node1);
    printf("合并之前链表2为>:");
    ListNodePrint(node4);
    ListNode* newnode = mergeTwoLists2(node1, node4);
    printf("合并链表之后>:");
    ListNodePrint(newnode);
}
int main()
{
    test();
    return 0;
}

5. 分割链表

  • 题目

在这里插入图片描述


  • 思路,其中x = 3

在这里插入图片描述


在这里插入图片描述


6. 链表的回文结构

  • 题目:回文结构,左右对称,例如:1221,12121,12321

在这里插入图片描述


  • 思路1:创建新数组,保存链表中所有节点的值,然后再判断数组是否是回文结构即可
//练习题:链表的回文结构
#include <stdbool.h>
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;
bool chkPalindrome(ListNode* A) 
{
    int arr[900] = { 0 };
    int i = 0;
    ListNode* pcur = A;
    //遍历链表,将链表的值放入数组
    while (pcur) 
    {
        arr[i++] = pcur->val;
        pcur = pcur->next;
    }
    //判断数组是否为回文结构
    int left = 0;
    int right = i - 1;//这里i=sizeof(arr)
    while (left < right) 
    {
        if (arr[left] != arr[right])
            return false;
        left++;
        right--;
    }
    return true;
}
  • 虽然这种方法一定程度帮助我们解题,但该方法受到链表长度的限制,而且严重存在浪费空间

  • 思路2:反转链表中间节点之后的一段链表,然后对比前后两段
#include <stdbool.h>
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;
//找链表中间节点
ListNode* middleNode(struct ListNode* head) 
{
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
//反转中间节点之后的链表
ListNode* reverseList(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;
}
bool chkPalindrome(ListNode* A) 
{
    //找中间节点
    ListNode* mid = middleNode(A);
    //反转中间节点之后的链表
    ListNode* right = reverseList(mid);
    ListNode* left = A;
    //遍历链表
    while (right) 
    {
        if (left->val != right->val) 
        {
            return false;
        }
        left = left->next;
        right = right->next;
    }
    return true;
}

在这里插入图片描述


  • 思路3:逆序链表,然后两个链表对比
//思路3:新建一个链表,作为逆序旧链表的头,然后对比两个链表
#include <stdbool.h>
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;

//逆序链表
ListNode* reverseList(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;
}
//判断链表是否为回文结构
bool chkPalindrome(ListNode* A) 
{
    //逆序链表
     ListNode* newNode = reverseList(A);
     ListNode* pcur = A;
     while (pcur)
     {
         if (pcur->val != newNode->val)
             return false;
         pcur = pcur->next;
         newNode = newNode->next;
     }
     return true;
}

7. 相交链表

  • 题目

在这里插入图片描述


  • 思路

在这里插入图片描述


//相交链表
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;

typedef struct ListNode ListNode;
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) 
{
    ListNode* pa = headA;
    ListNode* pb = headB;
    int sizeA = 0, sizeB = 0;      //计数
    //遍历链表
    while (pa)
    {
        pa = pa->next;
        sizeA++;
    }
    while (pb)
    {
        pb = pb->next;
        sizeB++;
    }
    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 (shortList == longList)
            return shortList;
        //不等继续向后走
        shortList = shortList->next;
        longList = longList->next;
    }
    //遍历完链表未找到相交节点
    return NULL;
}

8. 判断环形链表

  • 题目

在这里插入图片描述


在这里插入图片描述


//判断环形链表
#include <stdbool.h>
typedef struct ListNode ListNode;
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;
 
bool hasCycle(struct ListNode* head)
{
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇
        if (slow == fast)
            return true;
    }
    //走到头未相遇
    return false;
}
  • 思考:为什么在环形链表中,快指针走两步,慢指针走一步,两个一定会相遇?

在这里插入图片描述


  • 当然fast为3步、4步等都是可以追上的

在这里插入图片描述


9. 返回环形链表的入环节点

  • 题目

在这里插入图片描述


在这里插入图片描述


//返回环形链表入环节点
typedef struct ListNode ListNode;
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}ListNode;

struct ListNode* detectCycle(struct ListNode* head)
{
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇
        if (slow == fast)
        {
            //定义pcur找入环节点
            ListNode* pcur = head; 
            while (pcur != slow)
            {
                pcur = pcur->next;
                slow = slow->next;
            }
            //pcur和slow相遇
            return pcur;
        }
    }
    //处理空链表
    return NULL;
}

在这里插入图片描述


  • 证明在带环链表中,相遇点和头节点到入环第一个节点的距离相等

在这里插入图片描述


10. 随机链表的复制

  • 题目

在这里插入图片描述


在这里插入图片描述


  • 思路

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


//随机链表的复制
typedef struct Node
{
	int val;
	struct Node* next;
	struct Node* random;
}ListNode;

typedef struct Node Node;
//申请节点
Node* BuyNode(int x)
{
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL)
        return -1;
    node->val = x;
    node->next = node->random = NULL;

    return node;
}
//拷贝节点
void AddNode(Node* head)
{
    Node* pcur = head;
    while (pcur)
    {
        Node* next = pcur->next; //保存pcur下一个节点
        //创建新节点,插入到pcur节点之后
        Node* newnode = BuyNode(pcur->val);
        newnode->next = next;
        pcur->next = newnode;

        //pcur走向下个节点
        pcur = next;
    }
}
//设置random
void Setrandom(Node* head)
{
    Node* pcur = head;
    while (pcur)
    {
        Node* copy = pcur->next;
        if (pcur->random)
            copy->random = pcur->random->next;
        pcur = copy->next;
    }
}
struct Node* copyRandomList(struct Node* head)
{
    if (head == NULL)
    {
        return head;
    }
    //1.拷贝节点
    AddNode(head);
    //2.安置random
    Setrandom(head);
    //3.断开链表
    Node* newHead, * newTail;
    Node* pcur = head;
    newHead = newTail = pcur->next;
    while (newTail->next)
    {
        pcur = newTail->next;
        newTail->next = pcur->next;
        newTail = newTail->next;
    }
    return newHead;
}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;