算法你学废了么 - Day3

链表的概念

什么是链表,链表是一种通过指针串联在一起的一种线性结构,每一个结点都可以分为数据域和指针域(指针域负责存放下一个结点的指针),最后一个结点的指针域指向NULL结点,我们通常将链表的入口称为链表的头结点也就是head。

链表的类型

链表按照类型可分为好多种,其中常用的有单链表,双链表,循环链表。单链表是指只能从前向后遍历的链表,要想找到目标结点的前一个结点只能通过遍历的方法来实现
单链表
双向链表的好处就是可以找到一个节点的前驱和后继并且时间复杂度为O(1),之所以可以这样是因为双向链表的指针域中存放了两个指针一个是前驱结点的指针,一个是后继结点的指针。
在这里插入图片描述
循环链表顾名思义就是可以循环的链表,它可以通过链表末端快速找到链表头结点,当然我们们如果想快速找到链表的末尾,我们可以使用双向循环链表来快速找到头和尾。
在这里插入图片描述
其中链表的种类是非常多的,如果按照带不带哨兵位来分的话又可以分为,带哨兵位和不带哨兵位两种,具体的在这里就不和大家讨论了,我们通过几道链表中典型的题目来了解下。

链表经典题目

移除链表元素

题目链接203.移除链表元素
在这里插入图片描述
本题考查链表删除元素的基本操作,如果熟悉链表基本操作的话本体可以很轻松的就解决,但是给大家推荐一个小技巧,就是我们在对链表进行增删操作时可以给链表增加一个哨兵位,这样可以方便我们进行操作,下面一起来看下本题的操作。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
       ListNode* _head = new ListNode(0);//创建哨兵位
       _head->next = head;
       ListNode* prev = _head, *cur = _head;//prev负责找到目标结点的前驱节点
       while(prev != nullptr && prev->next != nullptr)
       {
       		cur = prev->next;
       		if(cur->val == val)
       		{
       			ListNode* tmp = cur;
       			prev->next = cur->next;
       			delete tmp;
       		}
       		else
       		{
       			prev = prev->next;
       		}
       	}
       	head = _head->next;//要更新一下head结点,如果要删除的节点为头结点的情况
       	delete _head;//释放哨兵位结点
       	return head;
};

反转链表

题目链接:206.反转链表
在这里插入图片描述
本题给大家介绍两种解法,一种方法是三指针法,顾名思义是利用了三个指针的移动来完成的,另一种方法则是来源于头插的思想,所以我们称之为头插法,方法一的大体思想是我们先建立三个指针,其中一个指针指向前一个结点,第二个指针指向当前节点,然后使当前结点的指针指向前一个结点,但是我们同样需要一个结点来保存当前结点的下一个结点,以便于下一回合我们可以找到该节点。
在这里插入图片描述
通过不断地移动cur直到cur为nullptr结束循环,此时prev指向的位置就是反转后链表新的头结点。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       ListNode* cur = head, *prev = nullptr, *next = nullptr;
       while(cur)
       {
       	  next = cur->next;
       	  cur->next = prev;
       	  prev = cur;
       	  cur = next;
       	}
       	return prev;
    }
};

第二种解法头插法,头插法就是利用了链表头插的思想,创建一个新的链表将原链表的结点从头以此插入新的链表,我们在进行头插更方便的做法是建立一个哨兵位,这里我们也同样建立一个哨兵位。下面是具体代码。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       ListNode* newnode = new ListNode(0);
       ListNode* cur = head, *next = head;
       while(cur)
       {
       		next = cur->next;
       		cur->next = newnode->next;
       		newnode->next = cur;
       		cur = next;
       	}
       	return newnode->next;
	}
};

两两交换链表中的结点

题目链接:24.两两交换链表中的结点

在这里插入图片描述
本题是两两交换链表中的两个节点,相当于反转一个只有两个结点的链表,故本题我们采用和反转链表相似的解法,首先我们建立一个哨兵位,然后我们开始反转链表,因为链表毕竟不只有两个结点所以我们要保存后面的结点,然后重复两个结点反转的过程,直到链表结尾,下面我们来看一下具体的代码。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* newnode = new ListNode(0);
        newnode->next = head;
        ListNode* cur = newnode;
        while(cur->next != nullptr && cur->next->next != nullptr)
        {
        	ListNode* tmp = cur->next;
        	ListNode* tmp1 = tmp->next->next;
        	cur->next = cur->next->next;
        	cur->next->next = tmp;
        	tmp->next = tmp1;
        	cur= cur->next->next; //让上一次循环的末尾节点充当哨兵位
        }
    }
};

在这里插入图片描述上图是我手动模拟的上面代码的一个过程,大家可以结合上图在仔细分析一下过程。

以上就是Day3的全部内容,觉得有用的小伙伴可以一键三连!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值