算法通关村第一关——链表 (青铜)

1.单链表的概念

1.1什么是链表

如下图,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系的存储结构就是链表。

核心是满足一个结点只有一个后继结点。

1.2结点的概念

如下图,单链表的每个基本单位都是这样的结构,即结点

每个结点都包括数据域和指针域,其中数据域用来存放元素的值,指针域用来指向该结点的后继结点。

例:链表{1,2,3}的结构就如下图。

所以,对于单链表,只要知道了第一个结点,就可以通过遍历访问整个链表,因此,第一个结点非常重要,一般称为头结点。

2.单链表的构造与销毁

2.1单链表结点的定义

struct Listnode{
    int val;//代表数据
    struct Listnode* next;//结点类型的指针,指向后继元素
};

2.2示例构造单链表

创建一个结构为{1,2,3,4,5}的单链表,函数如下

//创建一个结构为{1,2,3,4,5}的单链表
struct ListNode* initLink() {
	//创建头指针
	struct ListNode* head = NULL;
	//创建头结点
	struct ListNode* temp = new struct ListNode;
	temp->val = 1;
	temp->next = NULL;
	//头指针指向头结点
	head = temp;

	//创建剩下的结点
	for (int m = 2; m <= 10; m++) {
		struct ListNode* a = new struct ListNode;
		a->val = m;
		a->next = NULL;
		//每创建一个结点,就将其连上,并更新temp至最新创建的结点
		temp->next = a;
		temp = a;
	}
	//返回头指针
	return head;
}
    

    

2.3单链表的销毁

单链表的销毁也是逐一销毁,用一个指针记录本次循环要销毁的结点,然后用temp指针向后移动,指向下一次要销毁的结点。//可以在销毁前输出结点的值,方便检查

//销毁链表
void destroyList(struct ListNode* head) {
	struct ListNode* temp = head;

	//释放链表的所有结点
	while (temp) {
		struct ListNode* d = temp;
		//cout << temp->val<<"->";
		temp = temp->next;
		delete d;
	}
	temp = NULL;
}

3.单链表的遍历

对于单链表的遍历,就是一个一个逐一向后访问,重要的是要记得保留好头指针,不要只顾着向后遍历最后将头指针丢了。

3.1打印链表所有结点的值

//打印链表所有结点的值
void printList(struct ListNode* head) {
	struct ListNode* temp = head;

	while (temp) {
		cout << temp->val<<"->";
		temp = temp->next;
	}

}

3.2获取链表的长度

//获取链表的长度
int getLength(struct ListNode* head) {
	//若传入的是空链表,return 0
	if (head == NULL) return 0;

	int length = 0;
	struct ListNode* temp = head;
	while (temp) {
		length++;
		temp = temp->next;
	}
	return length;
}

4.单链表的插入

单链表的插入共分以下三种情况

4.1在单链表的表头插入

只需将新结点的后继指针指向原头结点,并将头指针更新为新结点//此处是易错点,容易将这一步漏掉

示意图如下

4.2在单链表的中间插入

在中间插入比较复杂,要注意操作的顺序,我们需要通过遍历找到目标位置的前一个位置,所以在循环遍历的时候循环判断要用current->next来判断位置。

如下图所示,要想将新结点插在15与7之间,要使current=node(15),接着先将new->next=node(15)->next,即node(7),然后将node(15)->next=new,顺序不能颠倒。

 

4.3在单链表的尾部插入

在单链表尾部插入只需找到原来的尾结点,然后将原尾结点->=new,就可以了。

在实际代码实现时,其实可以将中间插入和尾部插入归为一类,因为尾部插入其实就是中间插入的特殊情况。

//在单链表插入新结点
struct ListNode* insertNode(struct ListNode* head, struct ListNode* newNode, int position) {
	//若要插入的是空链表,直接返回newNode
	if (head == NULL) return newNode;

	int size = getLength(head);
	if (position<1 || position>size + 1) {
		cout << "位置参数越界" << endl;
		return head;
	}
	//在表头插入
	if (position == 1) {
		newNode->next = head;
		head = newNode;
		return head;
	}

    //在表中或链表尾插入
	int count = 1;
	struct ListNode* cur = head;
	//使cur指向position-1的结点
	while (count < position-1) {
		count++;
		cur = cur->next;
	}
	newNode->next = cur->next;
	cur -> next = newNode;

	return head;
}

5.单链表的删除

5.1删除头结点

先使用指针d保存head,接着将head=head->next,最后delete原先保存的d即可。

5.2删除尾结点

和插入结点一样,先需要找到尾结点的前一个结点,然后使用指针d保存cur->next(即要删除的尾结点),接着cur->next=NULL,最后delete原先保存的d即可。

5.3删除中间结点 

一样要找的删除结点的前一个结点,用指针d保存cur->next(即要删除的结点),然后cur->next=cur->next->next,最后delete原先保存的d即可。

同样,在实际代码实现时,其实可以将删除中间和删除尾部归为一类,因为删除尾部其实就是删除中间的特殊情况。

//在单链表删除结点
struct ListNode* deleteNode(struct ListNode* head, int position) {
	//若为空链表,返回NULL
	if (head == NULL) return NULL;

	int size = getLength(head);
	if (position<1 || position>size) {
		cout << "位置参数错误" << endl;
	}

	//删除头结点
	if (position == 1) {
		struct ListNode* d = head;
		head = head->next;
		delete d;
		return head;
	}
	//删除表中或尾结点
	int count = 1;
	struct ListNode* cur = head;
	//使cur指向position-1的结点
	while (count < position - 1) {
		count++;
		cur = cur->next;
	}
	struct ListNode* d = cur->next;
	cur->next = cur->next->next;
	delete d;

	return head;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值