首页 > 其他分享 >数据结构之链表(c语言版)

数据结构之链表(c语言版)

时间:2024-04-11 11:48:40浏览次数:22  
标签:Node head int 链表 数据结构 data 节点 语言版

链表是线性表,链表的特点就是可以动态增减元素。种类有单向链表、双向链表,循环链表

一、单链表

单链表的储存思想使用指针表示节点之间的逻辑关系,它的储存单元可以连续也可以不连续,每个储存单元需要储存信息和储存与后继节点的地址信息,储存单元又称之为节点。单链表由头指针唯一确定,整个单链表的操作必须由头指针开始。

链表的单位是一个一个节点,每个节点分为数据域和指针域,数据域存放数据,指针域存放指向下一个节点的指针(没有指针的语言存放的是对下一个节点的引用)。

头节点通常不放数据(也可以存放数据),尾节点指针域为空(循环链表不为空)。

单链表示意图:

在这里插入图片描述

c语言实现单链表1.0

还是1.0版本和2.0版本哈,2.0是后来增加的,更加健壮!

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

//定义节点
typedef struct list
{   int data;
    struct list *next;
    /* data */
}Node;


//节点初始化
Node *create_node(){
    Node *p=(Node *)malloc(sizeof(Node));
    //节点
    p->next=NULL;
    return p;
}

//建立链表,尾部增加
Node *link_list(int d[],int size){
    Node *head=create_node();
    Node *tail=head;        //尾节点,此时尾节点就是头节点
    for(int i=0;i<size;i++){
        Node *p=(Node *)malloc(sizeof(Node));
        p->data=d[i];
        p->next=NULL;
        tail->next=p;
        tail=p;//p变为尾节点
    }

    return head;
}

//追加
int append(Node *head,int e){
    Node *p=create_node();
    p->data=e;
    Node *s=head;
    while(s->next!=NULL){
        s=s->next;
    }
    s->next=p;
    return 1;
}

//长度
int Len(Node *head){
    Node *p=head;
    int count=0;
    for(;p!=NULL;count++){
        p=p->next;
    }
    return count;
    
}

//插入
int Insert(Node *head,int i,int e){

    if(i<1){
        printf("插入错误,程序终止");
        exit(0);
    }
    if(i>=Len(head)+1){
        //此时为追加
        append(head,e);
        return 1;
    }
    
    
    int n=1;
    Node *p=head;
    
    for(;n<i;n++){
        p=p->next;
    }
    Node *s=create_node();
    s->data=e;
    s->next=p->next;
    p->next=s;
    return 1;

}

//删除
int Delete(Node *head ,int i){

    if(i<1||i>Len(head)){
        printf("删除非法,程序终止");
        exit(0);
    }


    Node *f=head;//前指针
    Node *b;//后指针


    for(int n=1;n<i;n++){
        f=f->next;
        b=(f->next)->next;
    }

    Node *s=f->next;//此为被销毁的节点

    f->next=b;

    free(s);//释放内存

    return 1;
}

//查找
int Query(Node *head ,int i){

    if(i<1||i>Len(head)){
        printf("查找非法,程序终止");
        exit(0);
    }

    Node *p=head;//工作指针


    for(int n=1;n<i;n++){
        p=p->next;
    }

    int e=p->data;//查询的元素

    return e;
}
//修改
int Update(Node *head ,int i,int e){

    if(i<1||i>Len(head)){
        printf("修改非法,程序终止");
        exit(0);
    }


    Node *p=head;//工作指针


    for(int n=1;n<i;n++){
       p=p->next;
    }

    p->data=e;

    return 1;
}

//判空
int is_Empty(Node *head){
    if(head->next==NULL){
        return 1;
    }
    return 0;
}

//遍历链表
void display(Node *head){


    Node *p=head->next;//头节点没有数据

    printf("此时有%d个节点\n",Len(head));

    for(;p!=NULL;p=p->next){
        printf("The data is %d\n",p->data);
    }
}

//销毁链表
void Destory(Node *head){

    for(Node *p=head;head!=NULL;){
        head=head->next;
        free(p);
        p=head;
    }
    printf("链表已经销毁\n");
    exit(0);

}


这里把大部分功能都用函数表达了,有些功能用属性似乎更加简便,不过无伤大雅。

在主函数中测试一下功能:

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    
    display(p);

    append(p,18);
    display(p);

    Insert(p,2,37);
    Insert(p,4,23);
    Insert(p,14,37);
    display(p);

    Delete(p,2);
    display(p);

    int i=9;
    int e=Query(p,i);
    printf("查询的是第%d个元素,值为%d\n",i,e);

    Update(p,i,10);
    printf("修改了%d号元素,现在值为%d\n",i,Query(p,i));

    Destory(p);
}

测试结果:

创建链表
 此时有6个节点
The data is 2
The data is 1
The data is 5
The data is 11
The data is 6
此时有7个节点
The data is 2
The data is 1
The data is 5
The data is 11
The data is 6
The data is 18
此时有10个节点
The data is 2
The data is 37
The data is 1
The data is 23
The data is 5
The data is 11
The data is 6
The data is 18
The data is 37
此时有9个节点
The data is 2
The data is 1
The data is 23
The data is 5
The data is 11
The data is 6
The data is 18
The data is 37
查询的是第9个元素,值为37
修改了9号元素,现在值为10
链表已经销毁

c语言实现单链表2.0

从全局静态变量来表示头节点和节点数量,此时代码逻辑简单多了。

增删改查功能都没有实现,可以自行摸索。

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

//定义节点
typedef struct list
{   int data;
    struct list *next;
    /* data */
}Node;

static int count=0;//节点数量,代替了Len函数中的逻辑
static Node *head=NULL;//头节点


//节点初始化
Node *create_node(){
    Node *p=(Node *)malloc(sizeof(Node));
    //节点
    p->next=NULL;
    return p;
}

//建立链表,尾部增加
Node *link_list(int d[],int size){
    head=create_node();
    Node *tail=head;        //尾节点,此时尾节点就是头节点
    for(int i=0;i<size;i++){
        Node *p=create_node();
        p->data=d[i];
        p->next=NULL;
        tail->next=p;
        tail=p;//p变为尾节点
        printf("链表增加元素%d\n",d[i]);
    }

    count=size+1;//头节点也算
    printf("链表创建成功\n");
    return head;
}



//长度
int Len(){
    //原先的代码去掉,直接由count代替
    return count;
}



//判空
int is_Empty(){
    if(count==0){
        return 1;
    }
    return 0;
}

//遍历链表
void display(){


    printf("此时有%d个节点\n",count);
    Node *p=head->next;

    // for(;p!=NULL;p=p->next){
    //     printf("The data is %d\n",p->data);
    // }

    for(int i=1;i<count;i++){
        printf("The data is %d\n",p->data);
        p=p->next;
    }
}

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    display();
    
}

二、单向循环链表

循环链表有单向的也有双向的,其实就是尾节点的指针域指向了头节点,成为循环结构。这样从任意位置都可以遍历所有。

在这里插入图片描述

在单链表1.0的基础上稍作修改,一个单向循环链表就完成了。在指针的指向方面稍作修改就可以完成,部分方法就没有提供了,可以自行摸索。

单循环链表1.0

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

//定义节点
typedef struct list
{   int data;
    struct list *next;
    /* data */
}Node;


//节点初始化
Node *create_node(){
    Node *p=(Node *)malloc(sizeof(Node));
    //节点
    p->next=NULL;
    return p;
}

//建立链表,尾部增加
Node *link_list(int d[],int size){
    Node *head=create_node();
    Node *tail=head;        //尾节点,此时尾节点就是头节点
    for(int i=0;i<size;i++){
        Node *p=(Node *)malloc(sizeof(Node));
        p->data=d[i];
        p->next=head;//指向头节点
        tail->next=p;
        tail=p;//p变为尾节点
    }

    return head;
}


//长度
int Len(Node *head){
    Node *p=head;
    if(p==NULL){
        return 0; 
    }
    p=head->next;
    Node *s=head;
    int count=1;
    for(;p!=s&&p!=NULL;count++){
        p=p->next;
    }
    return count;
    
}


//判空
int is_Empty(Node *head){
    if(head->next==NULL){
        return 1;
    }
    return 0;
}

//遍历链表
void display(Node *head){


    Node *p=head->next;//头节点没有数据
    Node *s=head;//标记指针

    printf("此时有%d个节点\n",Len(head));

    for(;p!=s;p=p->next){
        printf("The data is %d\n",p->data);
    }
}


在主函数中测试一下:

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    
    display(p);//遍历循环链表

    Node * s=(p->next)->next;//指向其他节点的指针

    display(s);//其他节点遍历
}

结果如下,可以看到不是预期的结果,某个数据。。。是1995024,这是个啥,这是头节点的data,自动分配的。

创建链表
 此时有6个节点
The data is 2
The data is 1
The data is 5
The data is 11
The data is 6
此时有6个节点
The data is 5
The data is 11
The data is 6
The data is 1995024
The data is 2

这肯定不行啊,说好的头节点不放数据的,所以我们遇到头节点得跳到下一级。1.0版本的可以自己实现一下!

我们搞个2.0版本的。

单循环链表2.0

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

//定义节点
typedef struct list
{   int data;
    struct list *next;
    /* data */
}Node;

static Node *head=NULL;
static int count=0;


//节点初始化
Node *create_node(){
    Node *p=(Node *)malloc(sizeof(Node));
    //节点
    p->next=NULL;
    return p;
}

//建立链表,尾部增加
Node *link_list(int d[],int size){
    head=create_node();
    Node *tail=head;        //尾节点,此时尾节点就是头节点
    for(int i=0;i<size;i++){
        Node *p=create_node();
        p->data=d[i];
        p->next=head;//指向头节点
        tail->next=p;
        tail=p;//p变为尾节点
    }

    count+=size+1;//节点数量
    return head;
}

//查询
int query(int i){
    is_error(i);

    Node *p=head;

    for(int n=1;n<i;n++){
        p=p->next;
    }
    return p->data;

}

//是否非法
int is_error(int i){
 if(is_Empty()){
        printf("NULL,非法\n");
        return 1;
    }
    if(i<1||i>count){
        printf("查询超长,非法\n");
        return 2;
    }

    return 0;
}


//长度
int Len(){

    return count;
}


//判空
int is_Empty(){
    if(count==0){
        return 1;
    }
    return 0;
}


//遍历链表
void display(Node *p){


    printf("此时有%d个节点\n",count);
   
        for(int i=1;i<count;i++){
            if(p==head){	//判断是否是头指针
                p=p->next;
            }
            printf("The data is %d\n",p->data);
            p=p->next;
        }
    

}


主函数中测试一下:

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    
    display(p);//遍历循环链表

    p=p->next->next;//偏移

    display(p);//其他节点遍历
}
创建链表
 此时有6个节点
The data is 2
The data is 1
The data is 5
The data is 11
The data is 6
此时有6个节点
The data is 1
The data is 5
The data is 11
The data is 6
The data is 2

此时结果应该是正确的!

三、双向链表

双向链表即有两个指向的链表,通常指指向前节点和后继节点的链表。这样也是从任何部位都能遍历全部元素,

且查找元素时,可以向前查找或者向后查找,不必完全遍历全链表。

双向链表示意图:

双向链表

c语言实现双链表

在单链表2.0的基础上研究双链表。

先创建一个双向链表。

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

//定义节点
typedef struct list
{   int data;
    struct list *next;//双指针
    struct list *prev;
    /* data */
}Node;


static int count=0;//节点数量

static Node *head=NULL;//头节点


//节点初始化
Node *create_node(){
    Node *p=(Node *)malloc(sizeof(Node));
    //节点
    p->next=NULL;
    p->prev=NULL;
    return p;
}


//尾节点
Node *get_tail(){
    Node *tail=head;

    for(int i=1;i<count;i++){
        tail=tail->next;
    }

    return tail;
}

//建立链表,尾部增加
Node *link_list(int d[],int size){
    head=create_node();//头节点初始化
    Node *tail=head;        //尾节点,此时尾节点就是头节点
    for(int i=0;i<size;i++){
        Node *p=create_node();
        p->data=d[i];
        tail->next=p;
        p->prev=tail;
        tail=p;//p变为尾节点
    }
    count=size+1;
    return head;    //返回头节点
}


//长度
int Len(){

    return count;
}


//判空
int is_Empty(){
    if(count==0){
        return 1;
    }
    return 0;
}

//遍历链表,从头节点开始
void display(){

    printf("此时有%d个节点\n",count);

    Node *p=head->next;

    for(;p!=NULL;p=p->next){
        printf("The data is %d\n",p->data);
    }
}

//查找元素
//查找元素
Node* query(int i){
    if(error(i)){
        return NULL;
    }
     Node *p;
    if(i<=(int)(count/2)){//从头结点开始查找
       p=head;
        for(int n=1;n<i;n++){
            p=p->next;
        }
        return p;

    }else                   //从尾节点向前查找
    {
        p=get_tail();
        for(int n=0;n<count-i;n++){
            p=p->prev;
        }
        return p;   
    }
    return NULL;
}

//得到节点
Node *get_node(int i){
    Node *p=head;
    if(i==count){
        return get_tail();
    }
     if(error(i)){
        return NULL;
    }
    for(int s=1;s<i;s++){
        p=p->next;
    }
    return p;
}

//判断传入参数是否非法
int error(int i){
    if(is_Empty()){
        printf("链表NULL,非法\n");
        return 1;
    }
    if(i<1||i>count){
        printf("查询超长,非法\n");
        return 2;
    }

    return 0;
}



测试一下:

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    display();

    
    printf("查询的元素是%d\n",query(5)->data);
    printf("查询的元素是%d\n",query(2)->data);
    
 
}

此时可以查询。

创建链表
 此时有6个节点
The data is 2
The data is 1
The data is 5
The data is 11
The data is 6
查询的元素是11
查询的元素是2

再加入删除、更新、插入函数。

//插入
void insert(int i,int e){

    Node *s =query(i);

    Node *p=create_node();
    p->data=e;
    p->next=s;
    p->prev=s->prev;
    (s->prev)->next=p;
    s->prev=p;
    count++;
}
//删除
void delete(int i){
    Node *s=query(i);
    Node *p,*n;
    //修改指针指向
    p=s->prev;
    n=s->next;
    p->next=n;
    n->prev=p;
    free(s);    //销毁节点
    count--;//数量减少
}
//更新
void update(int i,int e){
    Node *p=query(i);
    p->data=e;
}

再次测试:

int main(){
    int a[5]={2,1,5,11,6};
    int size=sizeof(a)/sizeof(int);
    printf("创建链表\n ");
   
    Node *p=link_list(a,size);
    display();


    printf("查询的元素是%d\n",query(5)->data);
    printf("查询的元素是%d\n",query(2)->data);

    update(2,8);
    display();

    delete(2);
    display();

    insert(2,8);
    display();

}

如下所示:

创建链表
 此时有6个节点
The data is 2 
The data is 1 
The data is 5 
The data is 11
The data is 6
查询的元素是11
查询的元素是2
此时有6个节点
The data is 8
The data is 1
The data is 5
The data is 11
The data is 6
此时有5个节点
The data is 1
The data is 5
The data is 11
The data is 6
此时有6个节点
The data is 8
The data is 1
The data is 5
The data is 11
The data is 6

四、双向循环链表

双向循环链表的用处比较广泛、不过就是双向链表的基础上进行了循环。构造难度不大,可以参考单链表循环实现。

数据结构与算法学习目录

标签:Node,head,int,链表,数据结构,data,节点,语言版
From: https://www.cnblogs.com/cgl-dong/p/18128678

相关文章

  • 数据结构之栈(c语言版)
    栈(stack):在逻辑上是一种线性存储结构,它有以下几个特点:1、栈中数据是按照"后进先出(LIFO,LastInFirstOut)"方式进出栈的。2、向栈中添加/删除数据时,只能从栈顶进行操作。栈通常包括的三种操作:push、peek、pop。push--向栈中添加元素。peek--返回栈顶元素。pop--返......
  • 数据结构之队列(c语言版)
    队列(Queue):在逻辑上是一种线性存储结构。它有以下几个特点:1、队列中数据是按照"先进先出(FIFO,First-In-First-Out)"方式进出队列的。2、队列只允许在"队首"进行删除操作,而在"队尾"进行插入操作。队列通常包括的两种操作:入队列和出队列。队列的种类也很多,单向队列,双向队列,循......
  • 数据结构之二叉树(c语言版)
    之前的都是线性结构,而树结构在计算机应用中的应用更加广泛。linux中的目录结构,某些数据库的底层存储等,都是采用树结构进行构架的。树的概念线性表是一对一的关系,而树是一对多的关系。树的结点:包含一个数据元素及若干指向子树的分支;孩子结点:结点的子树的根称为该结点的孩子;双......
  • 数据结构之图(c语言版)
    图是比树更复杂的结构,树是一对多的关系,图是多对多的关系。一、基本概念1、定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。2、根据边是否有方向,将图可以划分......
  • 链表(java语言版)
    链表(Linkedlist)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。使用链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域......
  • 排序算法(c语言版)
    排序算法(c语言版)1、插入排序#include<stdio.h>//插入排序,升序voidinsertion_sort(intarr[],intlen){inti,j,key;for(i=1;i<len;i++){key=arr[i];//arr[i]为待插入的元素,保存在key中j=i-1;wh......
  • 数据结构与算法引言
    数据结构与算法引言数据结构和算法是计算机专业重要的基础课程。数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。算法简单来说就是解决问题的步骤。有了一个个数据结构和算法,我们可以编写出高质量的代码,高性能的产品。数据结构数......
  • C语言简单的数据结构:单链表的有关算法题(1)
    算法题重点在于思路,代码其实并不难,这里的每一题都提供多种思路,大家可以动手写一下,并找到更好的解题方法这里先介绍前三道题目:1.单链表相关经典算法OJ题1:移除链表元素2.单链表相关经典算法OJ题2:反转链表3.单链表相关经典算法OJ题4:链表的中间结点1.单链表相关经典算......
  • C语言简单的数据结构:单链表
    目录:1.单链表的概念及结构2.单链表的实现2.1单链表的定义、创建和打印2.11单链表的定义2.12单链表的创建2.13单链表的打印2.2单链表的头插和尾插2.21尾插2.22头插2.3单链表的头删和尾删2.31尾删2.31头删2.4单链表的查找2.5单链表指定位置的插入和删除2.51指定位置前......
  • (Java)数据结构——排序(第一节)堆排序+PTA L2-012 关于堆的判断
    前言本博客是博主用于复习数据结构以及算法的博客,如果疏忽出现错误,还望各位指正。堆排序(HeapSort)概念堆排序是一种基于堆数据结构的排序算法,其核心思想是将待排序的序列构建成一个最大堆(或最小堆),然后将堆顶元素与最后一个元素交换,再将剩余元素重新调整为最大堆(或最小堆),重复......