数据结构——单链表OJ题(上)

目录

一、移除链表元素

1.思路

2.注意

3.解题

二、反转链表

思路1:三指针翻转法

(1)注意

(2)解题

思路2:头插法

(1)注意

(2)解题

三、链表的中间结点

思路1:快慢指针法

(1)注意

(2)解题

思路2:两次遍历法

(1)注意

(2)解题

四、返回倒数第k个结点

1.思路:快慢指针法

2.注意

3.解题

五、合并两个有序链表

1.思路

2.注意

3.解题

六、链表分割

1.思路

2.注意

3.解题

七、写在最后


一、移除链表元素

1.思路

创建一个新链表,用指针pcur遍历原链表,对每个结点的数据进行判断,如果等于val则不做处理,如果不相等则尾插到新链表中。

2.注意

(1)原链表可能为空,那么返回NULL;

(2)最后记得将newTail指向NULL,在此之前需要判断newTail不能为空,因为如果为空,不存在newTail->next。

3.解题

cpp 复制代码
typedef struct ListNode ListNode;
struct ListNode*removeElements(ListNode* head , int val)
{
    if(head == NULL)//传入的该链表可能为空
    {
        return NULL;
    }
    //创建一个新链表
    ListNode* newHead = NULL;
    ListNode* newTail = NULL;
    //创建一个遍历该链表的指针
    ListNode* pcur = head;
    while(pcur)
    {
        if(pcur->val != val)
        {
            如果不等于val,尾插到新链表中
            if(newHead == NULL)
            {
                newHead = newTail = pcur;
            }
            else
            {
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }
        pcur = pcur->next;
    }
    //将尾结点指向NULL
    if(newTail)
    {
        newTail->next = NULL;
    }
    return newHead;
}

二、反转链表

思路1:三指针翻转法

创建三个指针n1,n2,n3分别指向NULL、head和head->next,改变指向,将n2指向n1,进行循环遍历原链表。

(1)注意

①原链表可能为空,那么返回NULL;

②n1为反转链表的头结点。

(2)解题

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

思路2:头插法

创建新链表,通过pcur遍历原链表,将数据头插到原链表中,得到反转链表。

(1)注意

①创建新指针保存pcur的下一个结点,否则pcur无法找到原位置。

(2)解题

cpp 复制代码
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
    //新链表
    ListNode* newhead = NULL;
    ListNode* pcur = head;
    while(pcur)
    {
        //保存当前结点的下一个结点
        ListNode* next = pcur->next;
        //头插新结点
        pcur->next = newhead;
        //更新头结点
        newhead = pcur;
        //移动到下一个结点
        pcur = next;
    }
    return newhead;
}

三、链表的中间结点

思路1:快慢指针法

初始时,快指针和慢指针都指向头结点。快指针一次经过两个结点,慢指针一次经过一个节点,当快指针走到链表结尾时,慢指针指向链表中间。

(1)注意

①分别分析一下链表长度为奇数和偶数时的情况(具体在代码中);

②最后返回指向链表中间的指针slow。

(2)解题

cpp 复制代码
typedef struct ListNode ListNode;
ListNode* middleNode(ListNode* head)
{
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast && fast->next)
    //由于fast->next要取next,因此不能为空
    //且不能交换顺序:否则链表长度为偶数的情况不能实现
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

思路2:两次遍历法

第一次遍历得到链表的长度len,由此得到半个长度mid,第二次遍历得到中间结点。

(1)注意

①mid为len/2+1(len为int类型);

②在第二个while循环中,pcur初始为head,每进行一次循环pcur就指向下一个结点。因此,当mid写为len/2,那么在第二次循环中就顺利可实现。

(2)解题

cpp 复制代码
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) 
{
    ListNode* pcur = head;
    int len = 0;
    //遍历得到链表的长度len
    while(pcur)
    {
        len++;
        pcur = pcur->next;
    }
    int mid = len / 2 ;
    //让pcur遍历链表,得到中间结点
    pcur = head;
    while(mid--)
    {
        pcur = pcur->next;
    }
    return pcur;
}

四、返回倒数第k个结点

1.思路:快慢指针法

初始时,快指针和慢指针都指向头结点。快指针先走k步,然后快、慢指针同时走,当快指针走到链表末尾时,慢指针走到倒数第k个结点。

2.注意

①返回的是倒数第k个结点保存的数据,而非指针;

②若k大于链表长度,fast会为空,此时返回NULL。

3.解题

cpp 复制代码
typedef struct ListNode ListNode;
int kthToLast(struct ListNode* head, int k)
{
    ListNode* fast = head;
    ListNode* slow = head;
    while(k--)
    {
        if(fast)
        {
            fast = fast->next;
        }
        else 
        {
            return NULL;
        }
    }
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow->val;
}

五、合并两个有序链表

1.思路

创建两个指针分别遍历两个链表,比较两个指针指向结点存储的数据,创建新链表,将小的数据存储在新链表中。

2.注意

①使用malloc创建非空链表,在尾插数据时不再需要判断newHead是否为NULL,避免代码冗余;

②当n1和n2其中一个为空会跳出循环,说明遍历完成。此时需要将另外一个尾插到新链表中。

③不必再令newTail指向空,因为此时尾结点不是newTail,而是后续尾插的n1或n2。

3.解题

cpp 复制代码
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* n1 = list1;
    ListNode* n2 = list2;
    while(n1 && n2)
    {
        if(n1->val < n2->val)
        {
            newTail->next = n1;
            newTail = newTail->next;
            n1 = n1->next;
        }
        else
        {
            newTail->next = n2;
            newTail = newTail->next;
            n2 = n2->next;
        }
    }
    if(n1)
    {
        newTail->next = n1;
    }
    if(n2)
    {
        newTail->next = n2;
    }
    ListNode* ret = newHead->next;
    free(newHead);
    newHead = NULL;
    return ret;
}

六、链表分割

1.思路

创建两个新链表(大于x和小于x的分别存储在两个链表中),用pcur遍历原链表,最后将大链表尾插在小链表后。

2.注意

①使用malloc创建两个非空链表,分别存储大于和小于x的数据。

3.解题

cpp 复制代码
typedef struct ListNode ListNode;
ListNode* partition(ListNode* pHead, int x) 
    {
        //大链表
        ListNode* greaterHead, *greaterTail;
        greaterHead = greaterTail = (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 
            {
                greaterTail->next = pcur;
                greaterTail = greaterTail->next;
            }
            pcur = pcur->next;
        }
        lessTail->next = greaterHead->next;
        greaterTail->next = NULL;
        ListNode* ret = lessHead->next;
        free(lessHead);
        free(greaterHead);
        lessHead = greaterHead = NULL;
        return ret;
    }

七、写在最后

一起加油,敬请期待"单链表OJ题(下)"~

相关推荐
DARLING Zero two♡11 分钟前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
graceyun1 小时前
C语言初阶习题【9】数9的个数
c语言·开发语言
波音彬要多做2 小时前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Noah_aa2 小时前
代码随想录算法训练营第五十六天 | 图 | 拓扑排序(BFS)
数据结构
KpLn_HJL2 小时前
leetcode - 2139. Minimum Moves to Reach Target Score
java·数据结构·leetcode
Schwertlilien3 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习
程序员buddha4 小时前
C语言从入门到放弃教程
c语言·开发语言
AAA.建材批发刘哥8 小时前
Linux快速入门-Linux文件系统管理
linux·运维·服务器·c语言·学习方法
AC使者8 小时前
5820 丰富的周日生活
数据结构·算法