链表_C语言快速入门与计算机二级备考

链表的概念

  • 为了克服数组连续存储难以修改的问题,采用链式存储,将分散的数据连接起来

  • 本质上是一种分为两部分的数据,一部分是普通数据,在最后是一个指针,指向下一个同类单元,这下一个单元又能储存数据,并且再指向下一个……而在最开始有一个指针指向第一个的开头,这个指针就是头指针

    这两部分分别为“数据域”和“指针域”,两部分结合,成为链表的一个结点

  • 链表中第一个节点是首元节点

    (有时在它之前还有一个头节点,不存放数据/或者只存放链表长度信息,只有指针指向首元结点,头指针此时指向头节点)

  • 下列代码构建的是单向链表

链表的创建

创建链表的结构

  • typedef struct _node {
    	int value;//数据域
    	struct _node *next;//指针域,这个指针指向struct _node类型的数据
    } Node;//用Node作为类型struct _node的名字
    
    //定义一个数据结构_list用以表达整个链表,将其链表的指针保存在全局的结构体中,便于之后使用函数与拓展
    typedef struct _list {
    	Node* head;
    	Node* tail;//例如增加一个总是指向链表末尾的tail指针,然后不必遍历去寻找尾结点
    } List;
    

创建链表的函数

  • #include <stdio.h>
    #include <stdlib.h>
    void add(List* pList, int number);
    int main(){
    	List list;
    	list.head = NULL;//先申请一个新的链表,将指向首元结点的头指针初始化
    	int number;//储存输入数据
    	do{
    		scanf("%d",&number);//为结点输入数据
    		if(number!=-1)//输入为-1时结束输入
    			add(&list,number);//传入list的指针与录入的结点数据
    	}while(number!=-1);//不断输入结点数据,输入为-1时结束
    }
    
    //将新建结点封装为函数add
    void add(List* pList, int number)//传入函数的是list的指针
    {
    //先创建一个新结点,写入其数据域与指针域
    	Node *p=(Node*)malloc(sizeof(Node));//为其分配内存
    	if(p){
    		p->value=number;//让输入的number成为新结点中的value
    		p->next=NULL;//作为新加入的结点,它是链表中的最后一个,故其指针无结点可指
    	}
    //同时,原先处在末尾的结点现在作为倒数第二个结点,其指针应该指向新加入的这个结点
    //要做到这一点,要先找到这个原先末尾的结点是谁
    	Node *last=pList->head;//创建一个新指针last用来找原末尾结点,先让它指向头结点
    	if(last)//当last不是NULL时(即链表不是只有头指针的空链表)
    	{
    		while(last->next){
    		//当结点last指向下一个结点的指针next不是NULL,就说明还有下一个结点
    			last =last->next;//让last变为下一个结点
    		//last一开始是head,不断循环到下个结点,当找不到时就是最后的结点了
    		}
    		last->next=p;//让last中的next等于p,即指向新加入的结点
    	}else{//要是last是NULL,表示整个链表是空的,只有一个指向NULL的头指针
    		pList->head=p;//所以新加入的p就应该是第一个结点
    	}
    

函数功能的拓展

  • //借助尾指针添加元素,不必使用遍历的方式来寻找最后一个结点,提高了效率
    	list.head = list.tail = NULL;//在申请新链表同时创建尾指针
    //修改后的函数add
    void add(List* pList, int number)
    {
    	Node *p=(Node*)malloc(sizeof(Node));
    	if(p){
    		p->value=number;
    		p->next=NULL;
    	}
    	Node* last = pList -> head; //先让last与head指向同个结点
    	if(last==NULL)//last无结点指,即head也无结点指,整个链表为空
    	{
    		pList -> head = p;
    		pList -> tail = p;//当链表为空,头尾指针都应指向这个新结点
    	}else{
    		last = pList -> tail;//让原本与head同指向的last变为与之前保存的tail同指向
    		last -> next = p;//原来的尾结点last指向新增结点p
    		pList -> tail = p;//新结点变为了新的尾结点,故让tail指向它
    	//通过head与last的交替指向,不断的在链表尾部连接新增的结点
    	//tail指针作为一个中间变量,用于全局地存放链表尾结点的地址
    	}
    }//如此,不使用遍历也可直接通过tail指针访问到链表尾部,便于新添加结点
    

对链表的操作

链表的遍历

  • void print(List *pList)//输出链表中每一项
    {
    	Node *p;//遍历链表的方法:先创建一个遍历用指针让它指向链表头
    	while (p!=NULL)
    	{
    		printf("%d",p->value);//递归中对链表各个结点的操作
    		p=p->next//不断递归指向下个结点,直到p为NULL(无下一个结点)
    	}
    }
    
    print(&list);//使用方法:传入所要遍历链表的指针
    

链表的搜索

  • //基于遍历
    int print(List *pList,number)
    {
    	Node *p=pList->head;
    	int isFound = 0;//是否找到对应结点的标志
    	while (p)
    	{
    		if(p->value == number){
    			printf("找到了");
    			isFound = 1;
    			break;
    		}
    		p=p->next;
    	}
    	if(!isFound){
    		printf("没有找到");
    	}
    	return isFound;
    }
    

结点的删除

  • 当要删除某一结点时,让前一结点的指针直接越过这个结点指向下一个

    之后,把这个结点的内存释放,防止错误

    //基于搜索,先找到要删除的结点
    //同时,需要知道要删除结点的前一个结点,故需要两个临时指针
    void del(List *pList)
    {
    	Node *p=pList->head;
    	Node *q=NULL;//用q指针来指向要删除结点的前一个结点
    	while (p)
    	//当p指向了下一个结点时,q等于p之前的值,也就是p的前一结点
    	{
    		if(p->value == number){
    			if(q){
    				q->next = p->next;
    			} else {
    				list.head = p->next;
    			}
    			free(p);
    			break;
    		}//找到要删除的结点后,让q绕过p直接指向p的下一项,再释放p
    		q=p;
    		p=p->next;//对下一结点进行同样的操作
    	}
    }
    //注意:在操作指针所指时(放在->左侧),指针不能为空指针
    //p可以确定不为NULL,因为p为NULL时循环终止,不进行操作
    //但是,若整个链表的第一项就是要删除的结点,则q指针将为NULL
    //故在q为NULL时,让p的下一项作为head
    

链表的清除

  • 使用完整个链表后,将之前为结点molloc分配的内存清除干净

    //同样使用两个临时指针,一个指向当前要删除的结点,一个指向下一个删除的结点
    void clear(List *pList)
    {
    	Node *q;
    	Node *p=pList->head;
    	while (p)
    	{
    		q=p->next;
    		free(p);
    		p=q
    	}
    }
    

posted on 2024-11-15 14:14  无术师  阅读(4)  评论(0编辑  收藏  举报