一.单链表的定义
链表是由一个个结点,通过指针串联起来的,一个结点是一个结构体,结构体中包括数据域和指针域。数据域中存储数据,指针域存储的是下一个结点的地址。
1.不带头结点的单链表
无论带头还是不带头,都应该先创建一个指针,这个指针可以指向一个结构体(结点)。
先定义一个结构体:
typedef struct s {
int data;
struct s* next;
}s;
这个结构体中有int类型的数据域 ,和指向下一个相同类型结构体的指针
什么叫不带头结点,就是初始化时只创建了一个指针,但是这个指针并没有指向一个结构体,而是指向NULL。
如下图所示:

相应的代码:
void init(s* ps) //ps为创建的结构体指针
{
ps = NULL;
}
2.带头结点的单链表
带头结点的单链表会比不带头结点的单链表有很多的好处
首先,什么叫带头结点的单链表,和不带头结点的区别是创建了一个新的结构体指针,这个指针不再指向NULL。而是指向一个结构体,而这个结构体里面的一个指针指向NULL.
如图所示:

相应的代码如下:
s* ps;
ps = (s*)malloc(sizeof(s));
ps->next = NULL;
二.单链表的插入,删除
1.前插操作
先输入的排在后面,而后输入的排在前面
如图所示:

从头指针往后看,先创建的排在后面,而后创建的排在前面。
代码实现如下:
int main()
{
s* p; //先创建一个指针
p = (s*)malloc(sizeof(s));
p->next = NULL;//创建一个带头结点的单链表
s* s1; //创建一个新指针,指向一个新的结构体
int x = 10;//要输入到一个结点的数据,可以是任何数字
s1 = (s*)malloc(sizeof(s));
s1->data = x;
s1->next = p->next;
p->next = s1;//注意的地方
return 0;
}
如图:

这里需要注意的是上面代码中两个指针改变的语句。上下两句顺序不可改变。
首先应用新创建的结构体里面的指针指向头指针指向的内容
然后再覆盖头指针只得地方,改为s1指针指向的地方,
这样新创建的结点就串在这个单链表中了
2.后插操作
从头指针往后看,先插入的排在前面,后插入的排在后面。
如图所示:

实现代码如下:
int main()
{
s* p;
p = (s*)malloc(sizeof(s));//和前插法不同,这里一开始并没有让头结点的指针指向NULL
s* p1;
s* end = p;//指向最后一个结点位置
int x = 10;
p1 = (s*)malloc(sizeof(s));
p1->data = x;
end->next = p1;//让最后一个结点的指针指向要再后面传进来的结点
end = p1;// p1 = end; //end指向新串进来的最后一个结点
end->next = NULL;//在插入完之后,最后才让最后一个结点里面的指针指向NULL
return 0;
}
如图:

3.在已有的一串单链表中后插一个结点
假设,现在获取了一串单链表中的一个结点,并知道指向这个结点的一个指针
那么我们就可以进行操作了
首先如图所示:

代码实现:
int main()
{
s* p;//表示获取的一个指针,我们这里假设就是头指针了
p = (s*)malloc(sizeof(s));
p->next = NULL;
s* s1;
int x = 10;//要插入的数据
s1 = (s*)malloc(sizeof(s));
s1->data = x;
s1->next = p->next;
p->next = s1;
return 0;
}
4.在已有的一串单链表中前插一个结点
这里我们其中有两种方法
第一种就是从头开始,然后往后查找,找到指定的结点,在前面插入一个新的结点
这种方法的时间复杂度是O(n)。
第二种方法是在这个结点后面插入一个新的结点,然后进行数据的交换,这样就可以实现前插操作
代码实现:
int main()
{
s* p;//假设这是我们获取的一个结点,要在这个结点前插入一个结点
p = (s*)malloc(sizeof(s));
s* s1;//创建的一个新的结点指针
s1 = (s*)malloc(sizeof(s));//新的结点
int x = 10;//要前插的数据
s1->next = p->next;
p->next = s1;//先实现后插操作
s1->data = p->data;//前一个结点的数据赋值到后面
p->data = x;//前面结点的数据被新的数据覆盖
return 0;
}
5.删除第i个结点的内容
首先,要删除第i个结点的内容,我们要找到第i-1个结点。因为我们要修改第i-1个结点的指针,指向第i+1个结点,然后把第i个结点的内容释放掉。
如图所示:

代码如下:
int main()
{
s* p;//
s* p1 = p;//用来标记查找到哪个节点了
int j = 0;//当前p指向的是第几个结点
int i = 5;//假设我们要删除第5个结点的内容
while (p1 != NULL && j < i - 1)//目的是找到第i-1个结点
{
p1 = p1->next;
j++;
}
if (p1 == NULL)
{
printf("i的数值不合法");
return 0;
}
s* shanchu = p1->next;
p->next = shanchu->next;//第i-1个结点指针指向第i+1个结点
free(shanchu);//释放掉这个结点
return 0;
}
这里我只是按照思路写的代码,并不能运行,因为我还没有初始化一个单链表
值得思考的是,这里我新建了一个shanchu这个指针,指向要删除的结点,为什么要新建一个,而不是直接用p1->next来进行操作
因为p1->next在过程中会发生改变。最后如果释放掉p1->next的值,会错误释放,因此需要特地来新建一个指针,指向要删除的结点。
三.查找结点
1.指定要查找第几个结点
代码如下:
int main()
{
s* p;//为头指针
s* p1 = p;//用来移动指向
int j = 0;//用来标记第几个结点
//假设要查找第i个结点
int i = 5;//我们随便设一个数字
while (p1 != NULL && j < i) //思考为什么是j<i
{
p = p->next;
j++;
}
return 0;
}
2.根据结点的数据内容查找结点
和上面的代码类似,时间复杂度都是O(n)
代码如下:
int main()
{
s* p;//p为头指针
s* p1 = p;//用来移动,防止头指针因移动导致找不到头结点
int j = 0;//用来标记找到了第几个结点
//假设要查找的这个结点的数据为5
int i = 5;
while (p != NULL && p->data != i)
{
p = p->next;
j++;
}
return 0;
}
1476

被折叠的 条评论
为什么被折叠?



