2024-5-19至2024-5-26数据结构知识点总结

一、无头非循环单链表

  1.链表功能

        无头非循环单链表主要包括增、删、插、改、显示的功能。每个链表的节点包含要存储的数据以及下一个数据存储的地址,最后一个节点的地址存储为空。特点:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等。

   2.学习过程

        作为初学者,在刚开始学的时候还是遇到很多麻烦的:指针,二级指针,断言的使用等等。(虽然老师课上讲的很详细,但有时候确实自己没能很好地理解)。后续跟着老师刷了一些力扣的题,也很卡(捂脸)。

   2.1在指针上遇到的麻烦

        什么时候用二级指针,什么时候用一级指针,想了好久。当需要使节点的地址发生变化时,就要用到二级指针。当需要对节点内的成员进行访问时,只需用到一级指针。

执行链表的插入函数

        这里通过传递节点地址的方式改变了原链表的构成,节点地址本身为一级指针,改变一级指针就要用二级指针(我自己这么理解的(捂脸))。

链表中执行显示功能的函数

        这里仅需要对每个节点的成员进行访问,一级指针就够了。

   2.2在assert上遇到的麻烦

       assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行。此为原文地址(c语言之assert()函数用法总结_assert函数c语言作用-CSDN博客

        我个人的理解为:判断断言内容是否为空。开始学的时候喜欢凡是用到指针的函数,开头都进行断言,但是在单链表的学习中明显不行了,

        单链表为空时可以打印,所以不可以对显示函数中传入的指针进行断言。

        执行删除功能时,要避免空链表,要保证节点不为空:assert(*head)。

   2.3刷题很慢

        做了几道老师课上提到过的题,总结:想不到,写不对(捂脸)。

203. 移除链表元素 - 力扣(LeetCode)

        自己写了半天,总是出错。

876. 链表的中间结点 - 力扣(LeetCode)

        代码质量不行

链表中倒数最后k个结点_牛客题霸_牛客网 (nowcoder.com)

        做了上面的题,学到正确的方法。

LCR 024. 反转链表 - 力扣(LeetCode)

        自己没写出来,跟着老师做的。

   3.总结

学习速度太慢了,这周学到的东西太少了,下周要提高效率,继续努力。

单链表头文件:
#pragma once
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
typedef  int SListDataType;

typedef struct SList {
	SListDataType data;
	//int size;
	//int Capacity;
	struct SList* next;
}ST;
void SListPrint(ST* phead);
void SListPushBack(ST** phead, SListDataType x);
void SListPopBack(ST** phead);
void SListPopFront(ST** phead);
void SListPushFront(ST** phead, SListDataType x);
ST* SListFind(ST* phead, SListDataType x);
void SListInsertFront(ST* pos, ST** phead,SListDataType x);//前插insert不能尾插
void SListInsertBack(ST* pos, ST** phead,SListDataType x);//后插
void SListEraseFront(ST** phead, ST* pos);//前删
void SListEraseBack(ST** phead, ST* pos);//后删
ST* BuyNote( SListDataType x);
void menu();
源文件(菜单功能没实现):
#pragma once
#include <stdio.h>
//#include <iostream>
//using namespace std;
#include "List.h"
void SListPrint(ST* phead)
{
	//assert(phead);断言只能解决空指针的问题,某些封装函数指针不可为空,所以匹配assert
					//但是,有些函数即便指针为空也是合理情况,这时就不能assert。
	ST* cur = phead;
	//顺序表对phead断言是为了保证顺序表不为空
	while(cur)
	{
		printf("%d-> ",cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
void menu()
{
	printf("**********单链表**********\n");
	printf("********1.数据展示********\n");
	printf("********2.数据插入********\n");
	printf("********3.数据删除********\n");
	printf("********4.数据清除********\n");
	printf("********5.数据查找********\n");
	//printf("********5.退出系统********\n");
	
	printf("**********单链表**********\n");
}
void SListPushBack(ST** phead, SListDataType x)
{
	assert(phead);//(别和空表弄混*phead是判断表是否为空)
	//assert(*phead);		//空链表可以尾插所以*phead可以为空
	ST* Tmp = BuyNote(x);
	if (*phead==NULL)
	{
		*phead = Tmp;//这里的Tmp是节点的地址,实质就是一个结构体的指针
	}
	else
	{
		ST* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = Tmp;
	}
}
ST * BuyNote(SListDataType x)
{
	ST* Tmp = (ST*)malloc(sizeof(ST));
	if (Tmp == NULL)
	{
		perror("Tmp NULL!");
		return NULL;
	}
	Tmp->data = x;
	Tmp->next = NULL;
	return Tmp;
}
void SListPushFront(ST** phead, SListDataType x)
{
	assert(phead);
	ST *Tmp=BuyNote(x);
		Tmp->next = *phead;
		*phead = Tmp;
}
void SListPopBack(ST** phead)
{
	assert(phead);//这里phead就是**phead,因为ST**是类型啊,int a,后续直接就a了,没有int。
	assert(*phead);//这里是加*是解引用,指针的地址解引用就是指针(节点的地址,这样就可以直接对原链表进行操作,避免了值传递问题)
					//在此基础上,要进行*phead断言是因为要对节点进行删除,每个地址就是一个成员,没成员就不能删除,所以要保证解出的地址不为空
	if (*phead==NULL)
	{
		perror("Phead NULL! ");
		return;
	}
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		ST* Tmp = (ST*)malloc(sizeof(ST));
		Tmp = *phead;
		ST* Pre = NULL;
		while (Tmp->next != NULL)
		{
			Pre = Tmp;
			Tmp = Tmp->next;
		}
		free(Tmp);
		Tmp = NULL;
		Pre->next = NULL;
	}
	/*ST* Tmp = (ST*)malloc(sizeof(ST));
	Tmp = *phead;
	while(Tmp->next->next!=NULL)
	{
		Tmp=Tmp->next;
	}
	free(Tmp->next);
	Tmp->next=NULL;(这种方式明显理解的更深了)
	*/
}
void SListPopFront(ST** phead)
{
	assert(phead);
	assert(*phead);
	if (*phead == NULL)
	{
		perror("Phead NULL! ");
		return;
	}
	else
	{
		ST*Tmp=(*phead);
		*phead = (*phead)->next;
		free(Tmp);
		Tmp = NULL;
	}


}
ST* SListFind(ST* phead, SListDataType x)
{
	//assert(phead);
	ST* Tmp = phead;
	while (Tmp)
	{
		if (Tmp->data == x)
		{
			printf("找到的地址为:%p\n",Tmp);
			return Tmp;
		}
		Tmp = Tmp->next;
	}
	printf("数据不包含在链表内!\n");
	return NULL;
}
void SListInsertFront(ST* pos, ST**phead, SListDataType x)//前插
{
	assert(phead);
	assert(pos);
	if (pos == *phead)
	{
		SListPushFront(phead,x);
	}
	else
	{
		ST* Tmp = BuyNote(x);
		ST* Tmp2 = *phead;
		
		//前插,关键是找到插入点的前一个位置
		while (Tmp2->next != pos)
		{
			Tmp2 = Tmp2->next;
		}
		Tmp2->next=Tmp;
		Tmp->next=pos;
	}
}
void SListInsertBack(ST* pos, ST** phead, SListDataType x)//后插
{
	assert(phead);
	assert(pos);
	if (pos == *phead)
	{
		SListPushFront(phead, x);
	}
	else
	{
		ST* Tmp = BuyNote(x);
		ST* Tmp2 = *phead;

		//后插,关键是找到插入点的下一个位置

		while (Tmp2!= pos)
		{
			Tmp2 = Tmp2->next;
		}
		Tmp->next = Tmp2->next;
		Tmp2->next = Tmp;
	}

}
void SListEraseFront(ST** phead, ST* pos)//前删
{
	assert(phead);
	assert(*phead);
	assert(pos);
	ST* cur = *phead;
	if (cur->next == NULL)
	{
		free(cur);
		cur = NULL;
	}
	else if (cur==pos)
	{
		perror("前删操作不能完成,请选择后删!");
	}
	else
	{
		while (cur->next != pos)
		{
			cur=cur->next;
		}
			/*ST* Tmp = *phead;
			while (Tmp != pos)
			{
				Tmp = Tmp->next;
			}
			cur->next = Tmp->next;
			free(Tmp);
			Tmp = NULL;*///自己写得,下面是人家写的,看看差距
			cur->next = pos->next;
			free(pos);
			//pos = NULL;//这里没用,写了就是基础不扎实,pos是指针,想把
					  //pos的地址置为空,就要用指向pos地址的指针(二级),再解引用。
					//可以外部置空
	}
}

void SListEraseBack(ST** phead,ST* pos)//后删,删的pos位置后面的值,和前删值不一样,离谱。
{
	assert(phead);
	assert(*phead);
	assert(pos->next);
	//自己写的:
	ST* Tmp = *phead;
	while (Tmp->next != pos)
	{
		Tmp = Tmp->next;
	}
	Tmp->next = Tmp->next->next;
	free(pos);
	//课上讲的:
	//Tmp=pos->next;
	//pos->next=pos->next->next;
	//free(Tmp);
	//Tmp=NULL;
}
测试文件:
#pragma once
#include "List.h"
void test()
{
	menu();
	ST* p = NULL;
	ST* l = NULL;
	SListPushBack(&p,1);
	SListPushBack(&p,2);
	SListPushBack(&p,3);
	SListPushBack(&p,4);
	SListPushFront(&p, 0);
	SListPushFront(&p, 9);
	SListPushFront(&p, 8);
	SListPopBack(&p);
	SListPopFront(&p);
	SListPopFront(&p); 
	ST* pp=SListFind(p,3);
	printf("%p\n",pp);
	SListInsertFront(pp,&p,3);
	//SListEraseFront(&p,pp);为什么放在这里会出问题?因为pp指向的内容已经被删了。
	SListInsertBack(pp,&p, 11);
	SListEraseFront(&p,pp);
	pp = NULL;
	ST* pp2 = SListFind(p, 3);
	SListInsertBack(pp2, &p, 11);
	//SListEraseBack(&p, pp);
	//pp=NULL;这里是后删,要执行需要重新调用一次Find函数。
	/*SListPopFront(&p);
	SListPopFront(&p);
	SListPopFront(&p);
	SListPopFront(&p);*/
	//SListPopFront(&p);
	SListPrint(p);
}
int main()
{
	test();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值