嵌入式学习一阶段——C语言:数据结构(1)

数据结构的基本概念

if you give someone a program , you will frustrate them for a day; if you teach them how to program,
you will frustrate them for a lifetime.
​
数据结构是计算机相关作专业的基础课程,但是也是一门不容易学好的一门课,它当中有很多费脑子的东西,
在之后上课了时,你若碰到困难或者不解的地方,都是非常正常的反应,就好像你坐飞机一样,在飞机晚点几个钟头,
上了飞机之后又颠簸一阵子,别大惊小怪,都是很正常的,只要你安全抵达就是成功。
数据结构是一门研究非数值计算的程序设计问题的操作对象,以及他们之间的关系和操作等相关问题的学科。

数据的一些基本的概念

数据(data):是对客观事物的符号的表示。在计算机科学中,是指所有能输入到计算机中并且可以被计算机程序处理的符号的综合。
    比如:
        我们现在常用的搜索引擎,一般会有网页,MP3,图片,视频等分配。mp3是不是就是声音数据,图片(图像数据),而我们网页其实指的就是全部数据的搜索,包括最终重要的数字和符号等文案。
​
数据元素:data element
    数据元素是数据的基本单位,在计算机中通常是作为一个整体进行考虑和处理的
        比如:
            在人类中,什么是数据元素??? 当然是人啊
            在畜类中,牛,羊,马....这些动物是不是就是畜类的数据元素
        
数据项:
    一个数据元素可以由若干个数据项组成。数据项是数据的不可分割的最小单位
    比如:
        人这样子的数据元素,当然是有鼻子,眼,嘴.....这些东西就是数据项,也可以是性别,年龄,出生地,联系电话,。。。。。 ,具体有哪些数据项,是不是得看你的系统来说。
数据对象:
    数据对象是性质相同的数据元素的集合,是数据的一个子集
    那么什么叫性质相同??? 是指数据元素具有相同的数量和类型的数据项,比如,刚才的例子,人是不是都有姓名,生日,性别等相同的数据项
​
数据:
    用户数据:用户需要存储的数据,如:学生信息(学号,姓名,成绩)
    元数据:用来管理用户数据的数据

数据结构

数据结构 不仅要保存数据,而且数据与数据之间的关系也要保存。根据数据元素之间的关系,通常可以分为如下四类:
    1、集合
        结构中的数据元素之间,除了在“同一个集合”外,没有其他的关系
    2、线性结构
        数据元素之间的关系是线性
        线性? 就是一条直线
    3、树形结构
        数据元素之间的关系是一层一层 /m/m/m 是树状
    4、图状结构
        图
    数据结构的形式定义为: 数据结构是一个二元组 Data_structure = (D,S)
        其中:  
            D 是数据元素的集合
            S 是 D 上关系(数据元素之间的关系)的集合
    结构定义中的“关系” 描述的是数据元素之间的逻辑关系,所以,又称为数据的逻辑结构
    “逻辑结构” :数据元素在某种“逻辑”(如:按成绩的降序排列的数据元素)的关系!!!
    “存储结构”:数据元素在计算机中表示(又称为映射)称为数据元素的物理结构或者存储结构!!!

讨论:

数据的 逻辑结构 与 存储结构之间的关系??
(1)、是不是数据第一次存进去,物理结构就固定了,之后对数据进行排序等等操作,改变的逻辑结构,物理结构没有变化
    逻辑结构是一个虚的,”想法“
    存储结构(物理结构)是实实在在存在,是真实存在
    A(1,zxf,100,A)
    B(2,zyz,80,B)
    C(3,haizi,99,A)
    D(4,nijie,82,B)
    E(5,xialing,78,C)
​
    逻辑关系:成绩的降序
                A        C          D        B          E
    存储结构  0x3000     0x3001     0x3002  0x3003      0x3004
​
    逻辑结构:按照学号的升序
                 A          B        C       D          E
    存储结构  0x3000     0x3001     0x3002  0x3003      0x3004
​
    可能 根据逻辑结构,去改变数据的存储结构,也可能是逻辑结构发生了变化,数据的存储结构没有变??
​
(2)、在计算机中存储的按哪个结构去存储呢?
    数据存储肯定会有 一个”存储结构“ , 同时可能对应多个”逻辑结构“
    逻辑结构有很多种,但是存储结构只有一种
​
(3)、存储结构就是物理存放的顺序关系吗????
        存储结构是指数据在存储器对应关系,逻辑结构会影响存储结构??
        可以影响,但是可以不影响,由你来定
        怎么存储?? 由你来定
        逻辑关系和存储关系是可以一致,也可以不一致!!

线性表

线性表,从名字你就可以感受到,是具有像线一样的性质的表。
26 个英文字母(abcd.....)  
    数据元素:单个字母
    A必须在B的前面,C必须在D的前面
班级人数(40,60,90,100)
    数据元素:每个班级的人数
 
”线性表“ 中的 数据元素 可以是各式各样的,但是在同一个线性表中的 数据元素 必须属于同一个类型 ,并且相邻的数据元素存在着序偶关系!
​
若将线性表记为:(a1,a2,a3,.....,ai.....an);
(1)、存在着唯一一个被称之为 ”第一个元素的“数据元素
(2)、存在这唯一一个被称之为”最后一个元素的“数据元素
(3)、除了”第一个“之外,集合中每一个元素有且仅有一个”前驱元素“
(4)、除了”最后一个“之外,集合中每一个元素有且仅有一个”后继元素“
例子:(1,3,5,7,2,4);
    上面这个例子就是一个线性表,现在我们要解决的问题??
    (1)、数据要保存   
            1,3,5,7,2,4
    (2)、关系是不是也要保存
            ”逻辑结构“
        方法一:
            int a[]={1,3,5,7,2,4};
            a[0] = 1;
            a[1] = 3;
            .......
​
        存储结构的前后关系,是不是正好可以描述你的“逻辑关系”
            地址在前的,逻辑关系也在前
            1的地址 0x3000
            3的地址 0x3004
            .......
            存储关系 与 逻辑关系是一致的把!!!!
        “用一组地址连续的存储空间,来依次存储逻辑上前后的数据”
​
        难道 "存储结构" 一定要用地址连续的吗??
            不一定
​
        方法二:
            存储空间 地址不连续
                在存储数据的同时,额外开辟一个指针空间,用来存储我们逻辑上的“下一个元素的地址”“上一个元素地址”
                “第一个元素的地址” “最后一个元素的地址”

线性表的物理结构的实现

顺序结构: 线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。“数组”

线性表的顺序存储结构,说白了,就是和上午的例子一样,就是在内存里面找一块地,通过占位置的形式,把一定的空间给占了,
然后再把相同数据类型的数据元素依次存放在一块地上。

链式结构: 保存数据元素的时候,不必要连续的地址空间,数据元素的存储单元不是连续的,但是要保存每一个数据元素的同时, 额外开辟一个指针空间,来保存它逻辑上的“上一个,下一个,或者两者”的元素的地址 链表

单链表

前言

刚刚我们讲到的线性表的顺序存储结构是有缺点的。最大的缺点就是就是插入和删除需要移动大量的元素,这显然是很浪费时间的,能不能像一个办法把这个问题解决呢??
​
让当中每一个元素之间我都留一个位置,这样子插入的时候,就不至于要去移动所有的位置。
​
(1,3,5,7,2,4)
存储数据元素 ‘1’    int
            &3   下一个元素的地址  *
存储数据元素 ‘3’    int
            &5     下一个元素的地址 *
........
以前我们在顺序结构中,每一个数据元素只需要存储数据元素信息是不是就可以了!现在我们的链表结构中,除了要存数据元素的信息之外,还要存它的后继元素的存储地址
因此表示每一个元素ai和其后继元素ai+1之间逻辑关系,对ai来说,它是不是还需要保存它的直接后继的位置
​
构造一个数据类型来把上面的这些数据保存
​
typedef int Elemtype;
typedef struct node
{
    Elemtype a;          //保存数据的 “数据域”
    struct node * next;   //保存下一个元素的地址 “指针域”
}Node;
​
然后n个Node(n个结点)链结成一个链表,即为线性表(a1,a2.....an)的一个链式存储结构,因为此链表的每一个结点中只包含一个指针域,所以我们叫他单链表。
​
对于一个线性表来说,我们是有头有尾的,当然链表也是一样,我们把链表中的第一个结点的存储位置称之为头指针!

练习:

用上述链式类型,存储(1,3,5,7,2,4),并且依次打印线性表中的各个元素
(1)、建立一个链表
(2)、遍历这个链表,依次打印每一个结点值

1、刚刚的代码 , 我们使用的是 “尾部插入法” : 按照用户输入的自然顺序,建立的链表
    能不能,按照用户输入的逆序建立链表??
    如:
        用户输入123
        创建的链表 321    “头部插入法”
​
        见Linklist文件夹
​
2、创建一个链表的时候,需要让他按照某个方式排序
​
    如:
        升序
    用户输入 1 5 3 4 0
    ====》
    1 3 4 5
        见Linklist文件夹
    
3、写一个函数求first指向的单链表的节点个数!
    见Linklist文件夹
​
需要默写的代码:
    尾插
        last->next = p;
        last = p;
    头插
        p->next = first;
        first = p;
    中间插
        pr->next = p;
        p->next = pk;
    在默写的过程,需要写出last first pr pk 的位置和含义,外加图片

如何在一个链表中,删除一个结点呢??
    “删除算法”也可以分为两步:
        (1)、找到要删除的节点
        (2)、干掉它
    删除:
        (1)、摘除它,使要删除的节点脱离原链表
        (2)、干掉他(free)
    见Linklist文件夹
​
如何在一个链表中,删除所有值为x的节点??
    见Linklist文件夹

找到之后,把data改成另外一个data就可以

找到即可

练习:

就地逆置一个单链表
    方法一:
        (1)一个一个摘除原链表的节点
        (2)把摘除的节点,用头插法加入新链表
            重复(1)(2)的操作,直到原链表上所有的节点都被摘除
​
    方法二:
        把原链表”一分为二“
            把第一个节点为第1链表,其余节点为第2链表
        p 指向第1链表的第一个节点
        q 指向第2链表的第一个几点,r指向q后面的那个节点
​
        首先 一分为二
        p = h
        q = h->next;
        p->next = NULL;
        while q 存在
            r = q->next;
            q->next = p;
                // q指向的节点为第1链表的第一个结点了
                // r指向的结点为第2链表的第一个结点了
            p = q;
            q = r;

#include <stdio.h>
#include <stdlib.h>

#include "Linklist.h"


/*
    @create_linlist:根据用户的输入,创建一个单链表,“按照用户的输入自然顺序创建的链表”
    @return{*}:返回,创建链表的第一个结点(节点)指针
*/
Node * create_linlist(void)
{
    int d;      //用来保存每次用户输入的数据  约定:输入0的时候结束输入
    Node * first = NULL;     //保存第一个结点
    Node * last = NULL;  //保存当前链表的最后一个结点
    Node * p = NULL;  //指向当前结点    

    while(1)
    {
        scanf("%d",&d);
        if(d == 0)
        {
            break;
        }
        p = malloc(sizeof(*p));   //malloc(sizeof(Node))
        p->data = d;
        p->next = NULL;

        if(first == NULL)
        {
            first = p;
            last = p;
        }
        else
        {
            last->next = p;
            last = p;
        }
    }
    return first;
}

/*
    @create_linlist_v2:根据用户的输入,逆序创建链表
    @return {*} : 返回,创建链表的第一个结点
*/
Node * create_linlist_v2(void)
{
    int d;      //用来保存每次用户输入的数据  约定:输入0的时候结束输入
    Node * first = NULL;     //保存第一个结点
    Node * last = NULL;  //保存当前链表的最后一个结点
    Node * p = NULL;  //指向当前结点    

    while(1)
    {
        scanf("%d",&d);
        if(d == 0)
        {
            break;
        }
        p = malloc(sizeof(*p));   //malloc(sizeof(Node))
        p->data = d;
        p->next = NULL;

        if(first == NULL)
        {
            first = p;
            last = p;
        }
        else
        {
            p->next = first;
            first = p;
        }
    }
    return first;
}

/*

*/
/*
    input : 7 5 4 6 3    output: 3 4 5 6 7
    //插入排序
    第一步:找到插入的位置
        在链表中找到第一个比 待插入的节点值 大的节点pk以及pk前面的那个节点pr
        pk的前面就是插入的位置
        p是待插入的节点

        Node * pk = NULL ; //pk指向链表中第一个比p大的节点
        Node * pr = NULL ; //pr指向pk前面的那个节点

        pk = first;
        while(pk!=NULL)
        {
            if(pk->data > p->data)
            {
                //哇塞,找到了
                break;
            }
            pr = pk;
            pk = pk->next;
        }
    
    第二步:插入操作
        if(没找到)
        {
            //所有的节点,都比 待插入的节点值 小,“尾插法”
            last->next = p;
            last = p;
        }
        else
        {
            //一进来就找到了
            if(pk == first)
            {
                //第一个节点就比你大 “头插法”
                p->next = first;
                first = p;
            }
            else        //从中间插入
            {
                pr->next = p;
                p->next = pk;
            }
        }
    
*/

/*
    @Insert:在h指向的升序单链表中,插入节点p,仍然使插入后的单链表有序
    @{Node *}h:指向单链表的第一个节点
    @{Node *}p:指向待插入的节点
    @return{*}:插入后链表的第一个节点指针
*/
Node * Insert(Node * h , Node *p)
{
    if(h == NULL)
    {
        return p;
    }
    if(p == NULL)
    {
        return h;
    }

    //分两步走
    //1、找位置
    Node *pk = h;
    Node *pr = NULL;
    while(pk!=NULL)
    {
        if(pk->data > p->data)
        {
            //哇塞,找到了
            break;
        }
        pr = pk;
        pk = pk->next;
    }
    if(pk==NULL)
    {
        pr->next = p;
    }
    else
    {
        //一进来就找到了
        if(pk == h)
        {
            //第一个节点就比你大 “头插法”
            p->next = h;
            h = p;
        }
        else        //从中间插入
        {
            pr->next = p;
            p->next = pk;
        }
    }
    return h;
}

/*
    @get_count:用来求链表的长度
    @{Node*}:指向链表的第一个节点
    @{return}int:返回链表长度大小
*/
int get_count(Node * p)
{
    if(p==NULL)
    {
        return 0;
    }
    return  1 + get_count(p->next);
}


/*
    “删除算法”也可以分为两步:
        (1)、找到要删除的节点的位置
        (2)、干掉它
    @delete_x:删除链表第一个值为x的节点
    @{Node *}h:指向单链表的第一个节点
    @{elemtype x}
    @{return *}
*/
Node * delete_x(Node *h , Elemtype x)
{
    Node *px = NULL;    //指向要删除的那个节点
    Node *pr = NULL;    //指向px前面的那个节点
    
    //1、找到要删除的节点
    px = h;
    while(px!=NULL)
    {
        if(px->data == x)
        {
            //哇塞,找到了
            break;
        }
        pr = px;
        px = px->next;
    }
    //压根没有找到,直接GG
    if(px == NULL)
    {
        return h;
    }
    //如果找到了,也要分两种情况
    if(px == h)
    {
        h = h->next;
        px->next = NULL;
        free(px);
    }
    else
    {
        //删除的节点是非第一个节点,说明原链表至少有两个以上的几点
        //px指向要删除的节点,pr指向px前面的那个节点
        pr->next = px->next;  // pr->next = pr->next->next;
        px->next = NULL;
        free(px);
    }
    return h;
}


Node * delete_all_x(Node *h , Elemtype x)
{
    Node *px = NULL;    //指向要删除的那个节点
    Node *pr = NULL;    //指向px前面的那个节点
    Node *ps = NULL;
    px = h;
    ps = h;
    while(1)
    {
        //1、找到要删除的节点
        px = ps;
        while(px!=NULL)
        {
            if(px->data == x)
            {
               
                //哇塞,找到了
                break;
            }
            pr = px;
            px = px->next;
        }
       
        //压根没有找到,直接GG
        if(px == NULL)
        {
            return h;
        }
        ps = px->next;
        //如果找到了,也要分两种情况
        if(px == h)
        {
            h = h->next;
            px->next = NULL;
            free(px);
        }
        else
        {
            //删除的节点是非第一个节点,说明原链表至少有两个以上的几点
            //px指向要删除的节点,pr指向px前面的那个节点
            pr->next = px->next;  // pr->next = pr->next->next;
            px->next = NULL;
            free(px);
        }
    }
    return h;
}


/*
    @reverse : 就地逆转链表
    @{Node *h} 
    @{return *}
*/
Node *reverse(Node *h)
{
    Node *first = NULL ;  //用来保存新链表的头指针
    Node *pk = h;         //工作指针
    while(pk)
    {
        h = pk->next;
        pk->next = NULL;

        if(first == NULL)
        {
            first = pk;
        }
        else
        {
            pk->next = first;
            first = pk;
        }
        pk = h;
    }
    return first;
}


/*
    @print_list : 打印链表
    @Node *p : 链表的第一个节点
    @return : 无
*/
void print_list(Node *p)
{
    for( ; p != NULL ; p = p->next)
    {
         printf("%d ",p->data);
    }
    printf("\n");
}

带头节点的单链表

单链表

data    data    data    data
next -->next -->next --->next
只需要找到单链表的第一个结点的指针:first
对单链表的的基本操作:”增删改查“就可以完成了
​
find_x:找到某个元素的代码
    while(1)
    {
        if(pk->data == x)
        {
            //哇塞,找到了
            printf("哇塞,找到了\n");
            break;
        }
        if(pk == NULL)
        {
            printf("GG,没有找到\n");
            return h;
        }
    }
find_last :  找到链表的最后一个结点
    p = h ;
    while(p)
    {
        if(p->next == NULL)
        {
            break;
        }
        pr=p;
        p = p->next;
    }
很多时候,我们经常可能需要知道一个链表中有多少个结点,或者求一个链表中最后一个结点....
    ====》 都需要通过第一个结点的指针,”遍历整个链表“
如:
    我能不能把链表的最后一个结点的指针保留在某个地方??
    我能不能设置一个变量用来保存链表中结点个数??
    ......
可以节省大量的遍历时间成本
​
Node *first;
Node *last;
int Nodenum;
......
====》我们为啥不把这些常用的遍历,构造成一个新的类型呢?
    ”头节点“
        ====》 带头节点的单链表

什么是头节点??

头节点是用来管理链表的结点,这个结点中一般包含常用管理链表的数据对象,如:指向链表的第一个结点指针,指向链表的最后一个结点指针,链表结点的数目....
我们原本用来保存数据的结点,我们称之为”数据结点“
​
头节点是唯一标识一个链表是否存在的标志
​
带头节点的链表:
    一个链表可以没有数据节点,但是必须有一个头节点
    没有数据结点,就说明这个链表是一个”空链表“
    没有头节点,就说明”这个链表就不存在“
​
头指针
    1、头指针是指向链表的第一个结点的指针,若链表有头节点,则是指向头节点的指针
    2、头指针具有标识的作用,所以进程使用头指针来冠以链表的名字
    3、无论链表是否为空,头指针都不为空,头指针是链表的一个必要元素
​
头结点
    1、头节点是为了操作的统一和方便而设立的,放在第一个数据结点之前,其数据域一般都没有意义(也可以用来存放链表界结点个数),但是它的指针域就可以存很多东西了(第一个结点的位置,最后一个结点位置。。。。。。。)
    2、有了头节点,对在第一个元素结点前插入结点和删除第一个结点,其操作与其他的结点的操作就一致
    3、头节点不一定是链表必须要素

带头节点的单链表

分别去定义有序 ”数据节点“ ”头节点“
​
//数据结点
typedef int Elemtype;
typedef struct node
{
    Elemtype data;          //保存数据的 “数据域”
    struct node * next;   //保存下一个元素的地址 “指针域”
}Node;
​
//头节点
typedef struct LinkedList
{
    Node *first;
    Node *last;
    int Nodenum;
}LinkedList;

带头节点的单链表的操作

1、写一个函数:函数的功能的创建一个带头节点的空链表 函数的返回值:返回这个链表的头节点

函数的内容:要给这个头节点分配空间
            是不是得把头节点的成员变量进行初始化

2、往一个带头节点的单链表上增加一个数据结点!!! 增

3、在一个带头节点的单链表,删除“数据结点”

作业

1、就地逆置一个单链表
    方法一:
        (1)一个一个摘除原链表的节点
        (2)把摘除的节点,用头插法加入新链表
            重复(1)(2)的操作,直到原链表上所有的节点都被摘除
​
方法二:
        把原链表”一分为二“
            把第一个节点为第1链表,其余节点为第2链表
        p 指向第1链表的第一个节点
        q 指向第2链表的第一个几点,r指向q后面的那个节点
​
首先 一分为二
        p = h
        q = h->next;
        p->next = NULL;
        while q 存在
            r = q->next;
            q->next = p;
                // q指向的节点为第1链表的第一个结点了
                // r指向的结点为第2链表的第一个结点了
            p = q;
            q = r;
2、将两个递增的有序链表合并成一个递增的有序链表!!要求结果链表仍然使用原来两个链表的空间,不另外占用空间,并且表中不允许出现重复的数据
3、往一个带头节点的单链表上添加一个数据结点
4、返回不带头节点的链表中倒数第k个结点的值

对带头结点的链表的一些操作

#include<stdio.h>
#include <stdlib.h>
#include"Linklisted.h"


List *creat_List(void)
{
    List * head=(List *)malloc(sizeof(List));
    head->first=NULL;
    head->last=NULL;
    head->nodenum=0;
    return head;
}


/*
@creat_linklist :根据用户输入,顺序创建链表,“尾插法”
@return {*}:返回,创建链表的头节点
*/
void creat_Linklist(List * l)
{
    int sum=0;//记录结点个数  
    
    Node *p=NULL;//保存结点
    int m;//我们约定输入0结束,即m==0是结束;
    while(1)
    {  
        scanf("%d",&m);
        if(m==0)
        {
            break;
        }  
        l->nodenum+=1;     
        p=(Node *)malloc(sizeof(Node));//开辟空间
        p->data=m;//保存数字到链表
        p->next=NULL;//指向下一个结点
        if(l->first==NULL)//如果是第一个结点
        {
            l->first=p;//头指针指向第一个结点
            l->last=p;//该结点是当前最后一个结点
        }
        else
        {
            l->last->next=p;//前一个结点的下一个指向当前结点
            l->last=p;//该结点是当前最后一个结点
        }     
    }     
}




/*
@creat_linklist_v2 :根据用户输入,逆序创建链表,“头插法”
@return {*}:返回,创建链表的第一个结点
*/
void creat_linklist_v2(List * l)
{
    Node *p=NULL;//保存结点
    int m;//我们约定输入0结束,即m==0是结束;】
    int sum=0;//记录节结点个数
    while(1)
    {  
        scanf("%d",&m);
        if(m==0)
        {
            break;
        }       
        l->nodenum++;
        p=(Node *)malloc(sizeof(Node));//开辟空间
        p->data=m;//保存数字到链表
        p->next=NULL;//指向下一个结点
        if(l->first==NULL)//如果是头指针
        {
            l->first=p;//头指针指向第一个结点
            l->last=p;//该结点是当前最后一个结点
        }
        else
        {
            p->next=l->first;//当前节点的下一个结点是第一个结点(逆序插入)
            l->first=p;//第一个结点的地址是当前结点
        }
       
    }
}

/*
    @Insert:在first指向的升序单链表中,插入节点p,仍然使插入后的单链表有序
    @{Node *}p:指向待插入的节点
    @{List *}l:链表的头节点
    @return{*}:插入后链表的第一个节点指针
*/
void Insert(List * l,Node *p)
{
    if(l==NULL)
    return;
    if(l->first==NULL)
    {
        l->first=p;
        l->last=p;
        l->nodenum=1;
        return ;
    }
    if(p==NULL)
    {
        return ;
    }
    Node *px=l->first;//保存大于p->data的数据的结点
    Node *pr=NULL;//保存px的前一个结点
    while(px)
    {
        if(px->data>p->data)//找到了
        {
            break;
        }
        pr=px;
        px=px->next;
    }
    if(px==NULL)
    {//没有找到.尾插法
        l->last->next=p;
        l->last=p;
    }
    else
    {
        if(px==l->first)//一进来就找到了
        {
            p->next=l->first;
            l->first=p;
        }
        else
        {
            pr->next=p;
            p->next=px;
                     
        }
    }
    l->nodenum+=1; 
}

/*
@delete_x:删除链表里的第一个数据等于x的结点;
@x:要删除的数据
@first:链表的头指针
@l:链表的头节点
@return:返回新链表的头节点
*/
void delete_x(List * l,Elemtype x)
{
    if(l==NULL||l->first==NULL)
    return ;

    Node * px=l->first;//指向待删除的结点
    Node * pr=NULL;//指向px前一个结点
    //1.找到这个要删除的结点
    while(px)
    {
        if(px->data==x)
        {
            l->nodenum-=1;
            break;
        }      
        pr=px;
        px=px->next;
    }
    if(px==NULL)
    {
        return ;
    }
    //2.删除它
    if(px==l->first)//找到了在头结点
    {
        if(l->first==l->last)
        {
            l->first=l->last=NULL;
            free(px);
        }       
        else
        {
            l->first=l->first->next;
            px->next=NULL;
            free(px);
        }
        
    }
    else if(px->next==NULL)//如果px是最后一个结点
    {
        pr->next=NULL;
        l->last=pr;
        free(px);
    }        
    else//在中间找到了
    {
        
        pr->next=px->next;
        px->next=NULL;
        free(px);
    }
}

/*
@delete_allx:删除链表里的全部数据等于x的结点;
@first:链表的头指针
@x:要删除的数据
@return:返回新链表的头节点
*/
void delete_allx(List *l,Elemtype x)
{

    if(l==NULL||l->first==NULL)
    return;

    Node * px=l->first;//指向待删除的结点
    Node * pr=NULL;//指向px前一个结点
    Node * ps=l->first;
    while(1)
    {
        px=ps;
        //1.找到这个要删除的结点
        while(px!=NULL)
        {
            if(px->data==x)
            {
                l->nodenum-=1;
                break;
            }          
            pr=px;
            px=px->next;
        }
        if(px==NULL)
        {
            return ;
        }
        //2.删除它        
        ps=px->next;
       if(px==l->first)//找到了在头结点
        {
            if(l->first==l->last)
            {
                l->first=l->last=NULL;
                free(px);
            }       
            else
            {
                l->first=l->first->next;
                px->next=NULL;
                free(px);
            }
        
        }
        else//在中间找到了
        {
            
            pr->next=px->next;
            px->next=NULL;
            free(px);
        }
    }
}

/*
@reverse_Linklist:将链表就地逆转;
@first:链表的头结点
@return:返回新链表的头节点
*/
void reverse_Linklist(List * l)
{
    /*链表“一分为二”*/
    l->last=l->first;
    Node * pk=l->first;//指向一链表头指针
    Node * ps=l->first->next;//指向二链表头指针
    Node * r=NULL;//指向二链表第二个结点
    pk->next=NULL;
    while(ps!=NULL)
    {
        r=ps->next;
        ps->next=pk;
        pk=ps;
        ps=r;
    }
    l->first=pk;
}


/*
@reverse_Linkedlist_v2:将链表就地逆转;
@p:链表的头指针,l:链表的头节点
@return:返回新链表的头节点
*/
void reverse_Linkedlist_v2(List * l)
{
    Node * p=l->first;
    Node * pr=l->first;//保存旧链表的前一个头结点
    l->last=l->first;
    l->first=NULL;//新链表的头指针
    while(pr)
    {
        p=pr->next;//旧链表的头指针右移
        pr->next=NULL;//前一个头指针与旧链表断开
        if(l->first==NULL)//如果是新链表的头指针
        {
            l->first=pr;
        }
        else//头插法
        {
            pr->next=l->first;
            l->first=pr;
        }
        pr=p;
    }
}


//合并两个链表
List * Mergelist(List * La,List * Lb)
{
    Node *pa = NULL;
    Node *pb = NULL;
    Node *pc = NULL;
    List * l=create_list();
    int f=0;
    if(La->first->data<Lb->first->data)
    {
        f=1;
        pc=La->first;
        pa=La->first->next;
        pb=Lb->first;
    }
    else if(La->first->data>Lb->first->data)
    {
        f=0;
        pc=Lb->first;
        pa=La->first;
        
    }
    else
    {
        f=1;
        pc = La->first;
        pa=La->first->next;
        pb=Lb->first->next;
    }
    l->first=pc;
    l->nodenum=La->nodenum+Lb->nodenum;
    while(pa&&pb)
    {
        if(pa->data < pb->data)
        {
            pc->next = pa;
            pc = pa;
            pa = pa->next;
        }
        else if (pa->data > pb->data)
        {
            pc->next = pb;
            pc = pb;
            pb = pb->next;
        }
        else
        {
            pc->next = pa;
            pc = pa;
            pa = pa->next;

            Node *q = pb->next;
            free(pb);
            pb = q;
        }
    }
    pc->next = pa ? pa:pb;
    if(f)
    {
        free(Lb);
        l->last=pc;
    }
    
    else
    {
        free(La);
        l->last=pc;
    }
    
    return l;
}


//将带头节点的链表a 分解成具有相同结构的两个链表b,c;c中结点>0且b中结点<0
void part_linklist(List* la,List* lb,List* lc)
{
    if(la==NULL||la->first==NULL)
    return;
    Node *p=la->first;
    Node *pr=NULL;//记录p的前一个地址
    while (p!=NULL)
    {
        if(p->data<0)
        {
            lb->nodenum+=1;
            if(lb->first==NULL)
            {
                lb->first=p;
                lb->last=p;
            }
            else
            {
                lb->last->next=p;
                lb->last=p;
            }
        }
        else
        {
            lc->nodenum+=1;
            if(lc->first==NULL)
            {
                lc->first=p;
                lc->last=p;
            }
            else
            {
                lc->last->next=p;
                lc->last=p;
            }          
        }
        pr=p;
        p=p->next;
        pr->next=NULL;
        
    }
    
}

/*
@delete_listnode:删除mink~maxk之间的数
@List* l:
@Elemtype mink:要删除的范围左界(不包含mink在内)
@Elemtype maxk:要删除的范围右界(不包含maxk在内)
@return:0,清空;1,未清空
*/
int delete_listnode(List* l,Elemtype mink,Elemtype maxk)
{
    if(l==NULL)
    return 0;
    Node * pk=l->first;//工作指针
    Node * pr=NULL;//记录pk前一个地址
    Node * min=NULL;//记录数据域==mink的地址 
    min=(Node*)malloc(sizeof(Node)); 
    Node * delet=NULL;
    delet=(Node*)malloc(sizeof(Node)); 
    while(1)
    {       
        while(pk!=NULL)
        {
            if(pk->data>mink&&pk->data<maxk)
            {
                break;
            }
            pr=pk;
            pk=pk->next;
        }
        if(pk==NULL)
        {
            return 1;
        }
        else
        {
            if(pk==l->first)//找到了,且为第一个
            {
                pr=pk;
                delet=pk;
                pk=pk->next;
                l->first=NULL;
            }
            else//找到了,在中间
            {
                min=pr;
                delet=pk;
                pr=pk;            
                pk=pk->next;
                min->next=NULL;
            }
        }
        while(pk!=NULL)
        {
            if(pk->data>=maxk)
            {
                break;
            }
            pr=pk;
            pk=pk->next;
        }
        if(pk==NULL)
        {
            free(delet);
            if(l->first==NULL)
            return 0;
            else
            return 1;
        }
        else
        {        
            if(min!=pk)
            {
                min->next=pk;
                pr->next=NULL;
            }
            else
            {
                pr->next=pk->next;
                pr->next=NULL;
                pk->next=NULL;
            }
            if(l->first==NULL)
            l->first=pk;
        }
        if(pk->data>=maxk)
        break;
    }
    free(delet);
    return 1;
}

//就地逆置数组
void reverse(int* a,int n)
{
    int *p,*q,t;
    p=a;
    q=a+n-1;
    while(p<=q)
    {
        t=*p;
        *p=*q;
        *q=t;
        p++;
        q--;
    }
}

//判断链表A是不是链表B的子链表
int judg_sublist(List* la,List* lb)
{
    Node* pa=la->first;
    Node* pb=lb->first;
    Node* pn=NULL;//保存pb的上一个地址
    pn=(Node*)malloc(sizeof(Node));
    while(pa!=NULL&&pb!=NULL)
    {
        pn=pb;
        if(pa->data==pb->data)
        {
            pa=pa->next;
            pb=pb->next;
        }
        else
        {
            pa=la->first;
            pb=pn;
            pb=pb->next;
        }
    }
    if(pa==NULL)
    return 1;
    else
    return 0;
}
//对链表进行排序(冒泡排序)
void smok_sortlist(List* l)
{
    Node* pk=l->first;
    Node* pr=NULL;
    Elemtype t;
    //int flag;
    for(;pk!=NULL;pk=pk->next)
    {
        for(pr=pk->next;pr!=NULL;pr=pr->next)
        {
            if(pr->data<pk->data)
            {
                
                t=pr->data;
                pr->data=pk->data;
                pk->data=t;
            }
        }
       
    }
}
//对链表进行排序(选择排序)
void select_sortlist(List* l)
{
    Node* pk=l->first;
    Node* pr=NULL;
    Node* min=pk;
    Elemtype t;
    //int flag;
    for(;pk!=NULL;pk=pk->next)
    {
        min=pk;
        for(pr=pk->next;pr!=NULL;pr=pr->next)
        {
            if(pr->data<min->data)
            {
                min=pr;
            }
        }
        if(min!=pk)
        {
            t=min->data;
            min->data=pk->data;
            pk->data=t;
        }
       
    }
}


//大数求和
void sum_list(List* l1,List* l2)
{
    Node* p1=l1->first;
    Node* p2=l2->first;
    Node* pr=NULL;//存放p2的前一个地址
    Node*p3=NULL;
    int x=0;
    p3=(Node*)malloc(sizeof(Node));
    while(p1!=NULL&&p2!=NULL)
    {      
        p1->data=p1->data+p2->data+x;
        if(p1->data>=10)      
        {
            x=p1->data/10;
            p1->data=p1->data%10;
        }
        p1=p1->next;
        pr=p2;
        p2=p2->next;
        pr->next=NULL;
        free(pr);
    }
    if(p1==NULL&&p2==NULL)
    {
        if(x!=0)
        {
            l1->Nodenum+=1;
            l1->last->next=p3;
            l1->last=p3;
            p3->data=x;
            p3->next=NULL;
            return;
        }
    }
    if(p1==NULL)
    {
        l1->last->next=p2;
        p2->data=p2->data+x;       
        if(p2->data>=10)
        {
            x=p2->data/10;
            p2->data=p2->data%10;
        }
        if(p2==NULL&&x!=0)
        {
            l1->Nodenum+=1;
            l2->last->next=p3;
            l1->last=p3;
            p3->data=x;
            p3->next=NULL;
            return;
        }
    }
    if(p2==NULL)
    {
        p1->data=p1->data+x;       
        if(p1->data>=10)
        {
            x=p1->data/10;
            p1->data=p1->data%10;
        }
        if(p1==NULL&&x!=0)
        {
            l1->Nodenum+=1;
            l1->last->next=p3;
            l1->last=p3;
            p3->data=x;
            p3->next=NULL;
            return;
        }
    }

}

//把一个数转化成链表(个位在第一个结点)
void create_numlist(int n,List* l)
{
    Node* p=NULL;  
    while(n)
    { 
        p=(Node*)malloc(sizeof(Node));
        p->data=n%10;
        p->next=NULL;
        n=n/10;
        if(l->first==NULL)
        {
            l->first=p;
            l->last=p;
        }
        else
        {
            l->last->next=p;
            l->last=p;
        }
    }
}




/*
@print_list:打印带头节点的单链表
@List * l:链表的头节点
@return :无
*/
void print_linklist(List* l)
{
    if(l==NULL||l->first==NULL)
    return;
    Node* p=l->first;
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值