单向链表的操作

链表

​ 链表正如它的名字一样是链式储存结构,它是一种线性结构只有唯一的‘上级’和唯一的‘下级’,它在内存上不一定是连续的,因此我们可以用它来解决无法开辟连续空间的问题。

链表有两种形式:单向链表和双向链表

​ 链表中的每一 个数据元素都是一个节点,在c语言中可以用结构体的方式呈现出来,它的每一个节点都封装了用来存储数据的数据域和存放内存地址的指针域。

单向链表:单向链表中只有一个指针域用来存储数据元素下一个节点的首地址。

双向链表:双向链表中有一个‘前趋指针域’和一个‘后继指针域’,与单向链表的区别在于每个节点内部多一个指针域成员,用于指向前一个节点 存储前一个节点的首地址。

单向链表

​ 在单向链表中每个节点都有数据域和指针域两部分,链表的第一个节点被称为头节点,最后一个节点成为尾节点,尾节点指针域指空。单向链表又根据头节点分为有头单链表和无头单链表,由于无头单链表不经常使用,所有在这里以有头单链表为例。有头单链表(以下称为单链表)的头节点是不进行数据存放的,头节点指向的节点进行数据的存放,在这里我们暂时性的把存放第一个数据的这个节点称之为首元节点,所以相较于无头单链表浪费了一个节点的空间。(无头单链表是没有首元节点的)

创建单链表

我们创建单链表的时候主要就是进行头节点的创建,只不过头节点的数据域不经常使用,主要使用指针域。


//单链表节点
typedef int sum;
typedef struct node{
    sum data;//数据域
    struct node *next;//指针域
}linklist;

	//定义头节点指针
	linklist *l = NULL;//指针置空避免成为野指针

	//动态分配内存 从堆区申请空间
	l = (linklist *)malloc(sizeof(linklist));

	//头结点初始化
	l->data = -1;//数据域
	l->next = NULL;//指针域

//链表创建函数
linklist *create(viod){
    
    linklist *l = NULL;
    
    l = (linklist *)malloc(sizeof(linklist));
    
    if(NULL==l){
        puts("创建失败!");
        return NULL;
    }

	l->data = -1;//头节点数据域不使用 可以赋任意值
	l->next = NULL;
    
    return l;//返回头节点首地址给调用者
    //调用者就拿到了堆区单链表首地址
}

单链表的插入操作

单链表的插入主要有三种方式:头部插入、尾部插入、任意位置插入

头部插入:

1.先将头节点指针域的地址拷贝一份到新节点指针域中,这样头节点与新节点同时指向了头结点的下一个节点。

2.再将新节点的首地址赋值到头节点的指针域中,这样头节点的指向就会发生改变,从而达到插入的目的。


//设头节点为t,新节点为n,头节点的下一节点为x
n->next=t->next;
t->next=n;

1.新节点的创建

​	linklist *n = (linklist *)malloc(sizeof(linklist));

2.新节点初始化

​	n->data = value;

​	n->next = NULL;

3.头部插入操作

​	linklist *q = l;//可以使用第三方变量来保证数据的完整性

​	n->next = q->next;//使新节点指向第一个数据元素

​	q->next = n;//改变头节点指向

尾部插入:

1.对于尾部插入操作,首先进行的是单链表的遍历,直到链表指针域为NULL时,我们就找到了尾节点。

2.找到尾节点之后,就只需要改变尾节点的指向就可以了。将新节点的首地址存储到尾节点的指针域中,这样新节点就成为了新的尾节点。


//设尾节点为t,新节点为n
t->next=n;

1.新节点创建
	linklist *n = (linklist *)malloc(sizeof(linklist));
2.新节点初始化
	n->data = value;//要插入的数据元素
	n->next = NULL;
	//作为尾结点的标志一定要初始化为 NULL,避免野指针
3.遍历到尾节点
	尾结点的特点是其指针域为NULL,我们可以把指针域为空作为遍历结束条件
	linklist *q = l;
	while(NULL!=q->next){
		q = q->next;//遍历到尾节点
	}
4.插入操作
	本质就是遍历到尾节点,然后将新节点的首地址赋值给尾节点的指针域
	q->next = N;

任意位置插入:

1.任意位置插入和头部插入类似,我们首先要定位到要插入位置节点的前一个节点。

2.然后更改新节点的指向,也就是将定位到所在位置节点的指针域中的地址拷贝给新节点的指针域中,把定位到所在位置的节点与新节点建立联系,也就是将新节点的首地址拷贝到相应的指针域即可。


//设定位到节点的前一个节点为t,新节点为n,后一个节点为x
n->next = t->next;
t->next = n;

1.定义一个动点指针(定位)
    linklist *q = l;
2.移动 q 的指向 (定位位置到要插入位置的前一个位置)
	for(;i<r;i++){//r为要插入的位置
     	q = q->next;
        if(NULL==q&&r>0){//判断表是否为空
		}
        if(NULL==q->next && i<r-1){
        //判断位置参数是否超出链表长度
		} 
	}
3.插入操作
    新节点n的指针域 = q指向的节点的指针域
	n->next = q->next;
    q指向节点的指针域 = 新节点n
    q->next = n;

单链表的遍历

1.判断表是否为空,确保表中至少有一个数据

2.要注意在有头单链表中,有效数据在头节点的下一个节点中存储,也就是从首元节点开始打印输出数据

3.单链表的尾节点指针域为空,而其他节点均不为空,所以可以使用指针域为空作为遍历的结束条件

4.定义一个指针指向首元节点(也就是存放有效数据的第一个节点)

5.当每次输出完数据后,指针将一直循环指向下一节点并输出数据,直到指针域为空时停止循环(注意:尾节点存放的数据也要输出)

单链表的排序

实现单链表排序 升序
   单链表排序算法,本质是交换数据元素的核心数据,没有必要交换节点(相对复杂)
   可以使用冒泡的思想对单链表进行排序操作
   1.设置一个定点 q 指向有效数据节点的第一个节点
       q = L;
   2.设置一个动点 p 指向 q 节点的下一个节点
       p = q->next;
   3.开始循环比较操作(以升序为例)
       需要设计两个循环(嵌套)
       外层循环决定比多少趟
       内层循环决定每一趟比多少次
       data_t mind;
       while(NULL!=q->next){//外层循环决定比多少趟
           q = q->next;
           p = q->next;        
           while(NULL!=p){//内层循环决定一趟比多少次
               if(q->data > p->data){
                   mind = q->data;
                   q->data = p->data;
                   p->data = mind;               
               } 
               p = p->next; //动点移动         
           }       
       }                  

单链表的释放

1.定义指针变量 q 作为引导动点 初始化为 头结点的指向
​        linklist *q = *l;
2.q变量作为动点开始移动到下一个位置
​        q = q->next;
3.将头结点指向的节点释放
​        free(*l);
4.重新给头节点赋值
​        *l = q;
如果我们直接使用头节点进行释放操作,头节点指向的节点之后所有节点信息都将丢失,最终会导致内存泄漏。
5.结束条件是 当 *l == NULL时释放结束

//(变量名引自创建操作)
//(健壮性判断引自头部插入操作)

//释放单链表
int free(linklist **l)
{
    //健壮性判断 
    if(NULL==l || NULL==*l){
    	puts("非法传入");
    	return -1;
    }    
    linklist *q = *l;    
    while(*l!=NULL){
    	q = q->next;
    	printf("%d \n",(*l)->data);
    	free(*l);
    	*l = q;
    }
    *l = NULL;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值