zoukankan      html  css  js  c++  java
  • 史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)

    1.准备工作

    首先包含头文件,定义链表结构体,产生随即链表的范围,定义全局头尾节点。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 10
    /*定义链表*/
    typedef struct Node 
    {
        int data;
        struct Node *next;  
    }Node;
    /*定义全局头尾节点*/
    Node *head = NULL;
    Node *end = NULL;
    

    2.创建链表

    /*根据传入的参数添加链表节点*/
    int CreatList(int a)
    {
        /*定义临时结构体并分配空间*/
        Node *temp = (Node *)malloc(sizeof(Node));
        if (temp ==NULL)
        {
            printf("malloc error!");
            return -1;
        }
        else
    	{
    		/*给数据类型赋值*/
    		temp->data = a;
    		temp->next = NULL;
    		/*如果链表长度为0*/
    		if (head == NULL)
    		{
    			head = temp; 
    			end = temp;    
    		}
    		else
    		{
    			end->next = temp;
    			end = temp;
    		}
        }  
    }
    

    3.打印链表

    /*打印链表*/
    void PrintList(Node *temp)
    {
        if(temp == NULL)
        {
            printf("Empty List!
    ");
        }
        while (temp)
        {
           printf("%d",temp->data);
           temp = temp->next;
    	   if(temp)
    	   printf("->");
        }
        printf("
    ");
    }
    

    4.在元素后面插入元素

    向链表中增添元素,根据添加位置不同,可分为以下 3 种情况:
    1.插入到链表的头部(头节点之后),作为首元节点;
    2.插入到链表中间的某个位置;
    3.插入到链表的最末端,作为链表中最后一个数据元素;

    虽然新元素的插入位置不固定,但是链表插入元素的思想是固定的,只需做以下两步操作,即可将新元素插入到指定的位置:
    a.将新结点的 next 指针指向插入位置后的结点;
    b.将插入位置前结点的 next 指针指向插入结点;

    例如,我们在链表 {1,2,3,4} 的基础上分别实现在头部、中间部位、尾部插入新元素 5,其实现过程如图 所示:

    在这里插入图片描述

    /*根据传入的数,在其后面增加元素*/
    int InsertListEnd(int index,int a)
    {
        if (head == NULL)
        {
            printf("Empty List!
    ");
            return 0;
        }
    	if (FindList(index)->next == FindList(a))
    		return 0;
    	else
    	{
    	  /*找到传入值的位置并保存*/
    		Node *temp = FindList(index);
    		/*分配空间存放新的传入的值*/
    		Node *pt = (Node *)malloc(sizeof(Node));
    		pt->data = a;
    		/*是否是最后一个元素*/
    		if (temp == end)
    		{
    			//尾巴的下一个指向新插入的节点
    			end->next = temp;
    			//新的尾巴
    			end = temp;
    		}
    		else
    		{
    			// 先连后面 (先将要插入的节点指针指向原来找到节点的下一个)
    			pt->next = temp->next;
    			//后连前面
    			temp->next = pt;
    			printf("The list after insert %d is 
    ",a);
    			PrintList(head);
    		}
    	}
    
    }
    

    5.在元素前面增加元素

    /*根据传入的数,在其前面增加元素*/
    int InsertListHead(int index,int a)
    {
        if (head == NULL)
        {
            printf("Empty List!
    ");
            return 0;
        }
    	/*要插入的位置就在原位*/
    	if (FindList(index)->next == FindList(a))
    		return 0;
    	else
    	{
    	   /*找到传入值的位置并保存*/
    		Node *temp = FindList(index);
    		/*分配空间存放新的传入的值*/
    		Node *pt = (Node *)malloc(sizeof(Node));
    		pt->data = a;
    		/*是否是第一个元素*/
    		if (temp == head)
    		{
    			//尾巴的下一个指向新插入的节点
    			pt->next = head;
    			//新的头
    			head = pt;
    		}
    		else
    		{
    			/*寻找到要插入位置的前驱节点*/
    			Node *pre = FindPreNode(temp);
    			pre->next = pt;
    			pt->next = temp;
    			printf("The list after insert %d is 
    ",a);
    			PrintList(head);
    		}
    	}
    
    }
    

    6.删除链表元素,要注意删除链表尾还是链表头

    从链表中删除指定数据元素时,实则就是将存有该数据元素的节点从链表中摘除,但作为一名合格的程序员,要对存储空间负责,对不再利用的存储空间要及时释放。因此,从链表中删除数据元素需要进行以下 2 步操作:

    1.将结点从链表中摘下来;
    2.手动释放掉结点,回收被结点占用的存储空间;

    其中,从链表上摘除某节点的实现非常简单,只需找到该节点的直接前驱节点 temp,执行一行程序:

    temp->next=temp->next->next;
    

    例如,从存有 {1,2,3,4} 的链表中删除元素 3,则此代码的执行效果如图 2 所示:
    在这里插入图片描述

    /*删除链表头*/
    void DeleteListHead()
    { //记住旧头
    	Node *temp = head;
    	//链表检测
    	if (NULL == head)
    	{
    		printf("Empty list!
    ");
    		return;
    	}
    
    	head = head->next; //头的第二个节点变成新的头
    	free(temp);
    }
    /*尾删除————删*/
    void DeleteListTail()
    {
    	if (NULL == end)
    	{
    		printf("链表为空,无需删除
    ");
    		return;
    	}
    	//链表不为空
    	//链表有一个节点
    	if (head == end)
    	{
    		free(head);
    		head = NULL;
    		end = NULL;
    	}
    	else
    	{
    		//找到尾巴前一个节点
    		Node *temp = head;
    		while (temp->next != end)
    		{
    			temp = temp->next;
    		}
    		//找到了,删尾巴
    		//释放尾巴
    		free(end);
    		//尾巴迁移
    		end = temp;
    		//尾巴指针为NULL
    		end->next = NULL;
    	}
    }
    /*删除链表任意元素*/
    void DeleteList(int a)
    {
       //链表判断 是不是没有东西
    	if (NULL == head)
    	{
    		printf("Empty list!
    ");
    		return;
    	}
    	//链表有东西,找这个节点
    	 Node *temp = FindList(a);
    	if (NULL == temp)
    	{
    		printf("%d not find
    ",a);
    		return;
    	}
    	//找到了,且只有一个节点
    	if (head == end)
    	{
    		free(head);
    		head = NULL;
    		end = NULL;
            printf("The list after delete %d is empty!
    ",a);
           
    	}
    	else if (head->next == end) //有两个节点
    	{
    		//看是删除头还是删除尾
    		if (end == temp)
    		{
    			DeleteListTail();
                printf("The list after delete %d is 
    ",a);
                PrintList(head);
    		}
    		else if (temp == head)
    		{
    			DeleteListHead();
                printf("The list after delete %d is 
    ",a);
                PrintList(head);
    		}
    	}
    	else //多个节点
    	{
    		//看是删除头还是删除尾
    		if (end == temp)
    			DeleteListTail();
    		else if (temp == head)
    			DeleteListHead();
    		else //删除中间某个节点
    		{	//找要删除temp前一个,遍历
    			Node *pt = head;
    			while (pt->next != temp)
    			{
    				pt = pt->next;
    			}
    			//找到了
    			//让前一个直接连接后一个 跳过指定的即可
    			pt->next = temp->next;
    			free(temp);
                printf("The list after delete %d is 
    ",a);
                PrintList(head);
    		}
    	}
        
    }
    

    7.根据传入的数值查询链表

    /*根据传入的数值,查询链表*/
    Node *FindList(int a)
    {
        Node *temp = head;
        if(head == NULL)
        {
            printf("Empty List!
    ");
            return NULL;
        }
      
        else
        {
           while (temp)
           {
               if (temp->data == a)
               {
                    printf("%d find!
    ",a);
                    return temp;
               }
               temp = temp->next;
           }
                printf("%d not find!
    ",a);
                return 0;
        }
        
    }
    

    8.修改链表元素

    /*修改链表元素,element为要修改的元素,modify为修改后的值*/
    void ModifyList(Node *phead,int element,int modify)
    {
    	Node *temp = phead;
    	while((temp!= NULL))
    	{
    		
    		if(temp->data == element)
    		{
    			temp->data	= modify;
    			
    		}	
    		temp = temp->next;
    	}
    }
    

    9.求链表长度

    /*求链表长度并返回*/
    int LengthList(Node *temp)
    {
        int length = 0;
        while (temp)
        {
            length++;
            temp = temp->next;
        }
        return length;
        
    }
    

    10.前驱,后继节点的查找

    Node *FindPreNode(Node *p)
    {
    	Node *temp = head;
    	/*寻找p的前驱节点*/
    	if(p == head)
    	{
    		printf("%d is head node
    ",p->data);
    		return NULL;
    	}
    	else
    	{
    		while((temp->next != p) && (temp !=NULL))
    		{
    			
    			temp = temp->next;
    
    		}
    		return temp;		
    	}
    
    }
    Node *FindNextNode(Node *p)
    {
    	Node *temp = head;
    	/*寻找p的后继节点*/
    	while(temp &&(temp != p))
    	{
    		temp = temp->next;
    
    	}
    	/*先不判断是否为尾节点,尾节点NULL也可以赋值*/
    	temp = temp->next;
    	return temp;
    	 
    }
    
    

    11.倒置链表

    /*方法一:倒置链表*/
    Node *InvertList(Node *phead)
    {
            if(phead == NULL || phead->next == NULL)
    		{
                    return phead;
            }
    		else
    		{
                    Node *p = phead;
                    Node *q = NULL;
                    Node *r = NULL;
                    while(p != NULL)
    				{
    						/*保存下一个节点*/
                            q = p->next;
    						/*让该节点指向上一个节点*/
                            p->next = r;
    						/*上一个节点走到当前节点*/
                            r = p;
    						/*当前节点走到下一个节点*/
                            p = q;
                    }
    				head = r;
                    return head;
            }
    }
    /*方法二:倒置链表*/
     Node *ReverseList(Node *phead)
        {
    		/*创建一个新链*/
    		/*两个指针,一个指向新的链表,一个指向单个断开的节点元素。连接起来*/
            Node *ptmp = NULL;
            Node *tmp = NULL;
    		/*处理链表为空*/
            if(NULL == phead)
    		{
                    printf("link is empty
    ");
                    return NULL;
            }else
    		{
    				/*将旧链上的结点链到新链上*/
                    while(phead != NULL)
    				{
                            tmp = phead;
                            phead = phead->next;
    						/*连接到上一次存下来的连表上。第一次时,ptmp为空,整个链表赋值给tmp后只剩下第一个元素*/
                            tmp->next = ptmp;
    						/*新的链表赋值给ptmp*/
                            ptmp = tmp;
                    }
            }
    		head = ptmp;
            return ptmp;
    }
    

    12.判断链表是否有环

    /*判断链表有环*/
    int Is_Circular(Node *phead)
    {
            if(phead == NULL || phead->next == NULL){
                    return 0;       
            }
    		/*快慢指针,当二者相等时,一定有环*/
            Node *p1 = phead;
            Node *p2 = phead;
            while(p1 != NULL && p2 != NULL){
                    p2 = p2->next; 
                    if(p1 == p2)
                            return 1;
                    p2 = p2->next;
                    p1 = p1->next;
            }
            return 0;
    }
    

    测试函数

    int main ()
    {
        int i = 0;  
        /*设置获得随机数的种子(固定代码,没有这句,随机数是固定不变的)测试可以不加*/
    	srand((int)time(0)); 
        for (i =5;i>0;i--)
    	CreatList(rand()%MAX);
    	// CreatList(i);
    	printf("新创建的的链表为:");
    	PrintList(head);
    	InsertListHead(4,10);
    	printf("在4前插入10后的链表为:");
    	PrintList(head);
    	InsertListEnd(4,10);
    	printf("在4后插入10后的链表为:");
    	PrintList(head);
    	DeleteList(0);
    	printf("删除0后的链表为:");
    	PrintList(head);
    	Node *p = FindList(7);
    	Node *q = FindList(4);
    	ModifyList(head,1,15);
    	printf("修改1为15后的链表为:");
    	PrintList(head);
    	ReverseList(head);
    	printf("反转后的链表为:");
    	PrintList(head);
    	printf("链表长度为:%d",LengthList(head));
        return 0;
    }
    

    测试截图
    在这里插入图片描述
    关于排序算法的讲解将在下节[单链表的5种排序算法]介绍。

    以上代码均为测试后的代码。如有错误和不妥的地方,欢迎指出。

    如遇到排版错乱的问题,可以通过以下链接访问我的CSDN。

    CSDN:CSDN搜索“嵌入式与Linux那些事”

    欢迎欢迎关注我的公众号:嵌入式与Linux那些事,领取秋招笔试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,笔试题目,简历模版等)和2000G学习资料。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    字符串匹配常见算法(BF,RK,KMP,BM,Sunday)
    JSP基本语法总结【1】(jsp工作原理,脚本元素,指令元素,动作元素)
    JUnit【1】断言用法之assertEquals/True/False/ArrayEquals
    软件测试基础配置
    前端入门20-JavaScript进阶之异步回调的执行时机
    前端入门19-JavaScript进阶之闭包
    前端入门18-JavaScript进阶之作用域链
    前端入门17-JavaScript进阶之作用域
    前端入门16-JavaScript进阶之EC和VO
    前端入门15-JavaScript进阶之原型链
  • 原文地址:https://www.cnblogs.com/dongxb/p/14142216.html
Copyright © 2011-2022 走看看