C语言数据结构[线性表]

在这里插入图片描述

一、顺序表

优点:查询操作比较快
缺点:数据存放必须是连续的;添加删除操作移动次数过多

A:动态分配

1.1 初始化

// 结构体存放初始数据
typedef struct student
{
    int* p;
    int len;
} STUDENT;

// 全局变量表创建时使用
int max_size=10;

// 初始化创建,用一个结构体存放动态创建内容的首地址
void arr_init(STUDENT* p){
    p->p = (int*)malloc(sizeof(int)*max_size);
    p->len=0;// 表写入数据的长度
}

1.2 插入

void arr_insert(STUDENT *p,int positon,int data){
    int i;
    if(positon>max_size){
        printf("超出允许的范围");
    }
    for(i=p->len;i>positon-1;i--){
        *(p->p+i)=*(p->p+i-1);
    }// 0 1 2
    *(p->p+i)=data;
    p->len++;
}

1.3 删除

void arr_remove(STUDENT* p,int positon){
    int i;
    if(positon>max_size){
        printf("超出允许的范围");
        return;
    }// 1 2 3
    for(i=positon-1;i<p->len-1;i++){
        *(p->p+i)=*(p->p+i+1);
    }
    p->len--;
}

1.4 释放

free(stu.p);

1.5 打印

void arr_printf(STUDENT p){
    int i;
    for(i=0;i<p.len;i++){
        printf("%d\n",*(p.p+i));
    }
}

1.6 示例程序

void main(){
    STUDENT stu;
    arr_init(&stu);
    arr_insert(&stu,1,151);
    arr_insert(&stu,2,15);
    arr_insert(&stu,3,1);
    arr_insert(&stu,4,15111);
    arr_remove(&stu,4);
    arr_printf(stu);
    free(stu.p);
}

B:静态分配

解释:单纯的把结构体中的指针换为数组

区别:分配位置大小固定

二.单链表

A:单链表

优点:相比于顺序表,添加删除操作比较快,比较方便
缺点:相比于顺序表,查询比较慢;只能单向按顺序访问

2.1 有头节点

注意:用有头节点写程序会很方便,本程序的头结构体与数据结构体不一致,造成程序更杂乱,普通使用头结构体与数据结构体相同即可
图解:
在这里插入图片描述

2.1.1 初始化
typedef struct student
{
    int data;
    int next;
} NODE;
// 初始化
NODE* list_init()
{
    NODE* p =(NODE*)malloc(sizeof(NODE));
    p->data=0;
    p->next=NULL;
    return p;
}
2.1.2 插入
// 按位置插入
void list_insert(NODE* head,int data,int position)
{
    int i;
    NODE* node = (NODE*)malloc(sizeof(NODE));// head 1 2 3
    for(i=1;i<position;i++){
        head=head->next;
    }
    if(head){
        node->data=data;
        node->next=head->next;
        head->next=node;
    }

}
2.1.3 删除
// 删除指定位置的内容
void list_remove(NODE* head,int position)
{
    int i;
    for(i=1;i<position;i++){
        head=head->next;
    }
    NODE* temporary=head->next;
    head->next=temporary->next;
    free(temporary);
}
2.1.4 打印
// 打印内容
void list_printf(NODE* p)
{
    p=p->next;
    while(p){
        printf("%d\n",p->data);
        p=p->next;
    }
}
2.1.5 主程序
void main()
{
    NODE*p = list_init();
    list_insert(p,5,1);
    list_insert(p,69,2);
    list_insert(p,6555,3);
    list_remove(p,2);
    list_printf(p);

    return 0;
}

2.2 无头节点

注意:无头节点,在涉及到第一位元素的操作时会变得很麻烦,代码同时更杂乱

// 初始化
typedef struct student
{
  	int a;
    int b;
    struct student* next;
} NODE;

NODE* list_init(int a,int b)
{
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=a;
    node->b=b;
    node->next=NULL;
    return node;
}
// 插入
NODE* list_insert(NODE* p,int a,int b,int position)// 带返回值主要是为了头插法,改变p的指向
{
    int i;
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=a;
    node->b=b;
    NODE* pos=p;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    if(position==1)
    {
        node->next=p;
        p=node;// 此时不用返回值,p在main里面并不会和此一样
        return p;
    }
    else
    {
        for(i=1; i<position-1; i++)
        {
            pos=pos->next;
        }
        node->next=pos->next;
        pos->next=node;
        return p;
    }
}
// 删除
void list_remove(NODE* p,int position)
{
    int i;
    NODE* pos=p;
    NODE* pos_one;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    if(position==1) //不能直接简单交换指针,可以换数据
    {
        pos_one=p->next; // 准备删除第二个,此作为中介
        p->a=p->next->a;
        p->b=p->next->b;
        p->next=p->next->next;
        free(pos_one);
    }
    else
    {
        for(i=1; i<position-1; i++)
        {
            pos=pos->next;// 即将删除位置前的一个
        }
        pos_one=pos->next;//post_one指针是要删除的内容
        pos->next=pos_one->next;// 把删除位置前的一个的指针,指向被删除指针指向的地址
        free(pos_one);
    }
}
// 释放
void list_del(NODE* p)
{
    NODE* node_one;
    NODE* node_two=p;
    while(node_two)
    {
        node_one=node_two;
        node_two=node_one->next;
        free(node_one);
    }
}
// 打印
void list_printf(NODE* p)
{
    NODE* pos=p;
    while(pos)
    {
        printf("%d %d\n",pos->a,pos->b);
        pos=pos->next;
    }
}
void main()
{
    int a=1200,b=1500;
    NODE* p = list_init(a,b);
    p=list_insert(p,1,2,2);
    p=list_insert(p,1115,212112,1);
    p=list_insert(p,15,212,1);
    list_remove(p,1);
    list_printf(p);
    list_del(p);
}

B:单循环链表

区别:可以从任意位置按照顺序去查询数据

代码细节:①初始化放置的头节点放置不是NULL而是指向自己②循环结束条件改为pos!=p

// 单循环链表
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
    int a;
    int b;
    struct student* next;
} NODE;
// 带头指针的初始化
NODE* list_init()
{
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=0;
    node->b=0;
    node->next=node;
    return node;
}
// 按位置插入
void list_insert(NODE* p,int a,int b,int position)
{
    int i;
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=a;
    node->b=b;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    for(i=1; i<position; i++)
    {
        p=p->next;
    }
    node->next=p->next;
    p->next=node;
}
// 打印内容
void list_printf(NODE* p)
{
    NODE* pos=p->next;
    while(pos!=p)
    {
        printf("%d %d\n",pos->a,pos->b);
        pos=pos->next;
    }
}
// 清空
void list_del(NODE* p)
{
    NODE* node_one;
    NODE* node_two=p->next;
    while(node_two!=p)
    {
        node_one=node_two;
        node_two=node_one->next;
        free(node_one);
    }
}
// 删除指定位置的内容
void list_remove(NODE* p,int position)
{
    int i;
    NODE* pos_one;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    for(i=1; i<position; i++)
    {
        p=p->next;// 即将删除位置前的一个
    }
    pos_one=p->next;//post_one指针是要删除的内容
    p->next=pos_one->next;// 把删除位置前的一个的指针,指向被删除指针指向的地址
    free(pos_one);
//    }
}
void main()
{
    NODE* p = list_init();
    list_insert(p,1,2,1);
    list_insert(p,1115,212112,2);
    list_insert(p,1115,2121,3);
    list_remove(p,1);
    list_printf(p);
    list_del(p);
}

三.双链表

A:双链表

优点:增加删除还是比顺序表方便,查询效率有提高,双向按顺序访问
缺点:使用的内存空间增大

代码细节:如果把移动指针指向目标上(而不是它前一个节点),注意null的处理

// 双链表
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
    int a;
    int b;
    struct student* pre;
    struct student* next;
} NODE;
// 带头指针的初始化
NODE* list_init()
{
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=0;
    node->b=0;
    node->pre=NULL;
    node->next=NULL;
    return node;
}
// 按位置插入
void list_insert(NODE* p,int a,int b,int position)
{
    int i;
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=a;
    node->b=b;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    for(i=1; i<position; i++)
    {
        p=p->next;
    }
    node->next=p->next;
    node->pre=p;
    p->next=node;
}
// 打印内容
void list_printf(NODE* p)
{
    NODE* pos=p->next;
    while(pos)
    {
        printf("%d %d\n",pos->a,pos->b);
        pos=pos->next;
    }
}
// 清空
void list_del(NODE* p)
{
    NODE* node=p->next;
    NODE* node_tem;
    while(node)// 1 2 3
    {
        node_tem=node;
        node=node->next;
        free(node_tem);
    }
    free(p);
}
// 删除指定位置的内容
void list_remove(NODE* p,int position)
{
    int i;
    NODE* pos_one=p->next;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    for(i=1; i<position; i++)
    {
        pos_one=pos_one->next;// 即将删除位置前的一个
    }
    pos_one->pre->next=pos_one->next; //这里引用的要删除的当前元素
    if(pos_one->next){ // 此种定位到要变动元素的方法,要防止末尾出现null的情况
        pos_one->next->pre=pos_one->pre;
    }
    free(pos_one);
}
void main()
{
    NODE* p = list_init();
    list_insert(p,1,2,1);
    list_insert(p,1115,212112,2);
    list_insert(p,1115,2121,3);
//    list_remove(p,3);
    list_printf(p);
    list_del(p);
}

B:双链表循环

代码细节:无需再注意NULL;注意循环条件改变;尾插法时头节点的pre变化;插入其它位置时pre的变化初始化时pre和next都指向头节点

// 双循环链表
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
    int a;
    int b;
    struct student* pre;
    struct student* next;
} NODE;
// 带头指针的初始化
NODE* list_init()
{
    NODE* node=(NODE*)malloc(sizeof(NODE));
    node->a=0;
    node->b=0;
    node->pre=node;
    node->next=node;
    return node;
}
// 按位置插入
void list_insert(NODE* p,int a,int b,int position)
{
    int i;
    NODE* node=(NODE*)malloc(sizeof(NODE));
    NODE* p2=p;
    node->a=a;
    node->b=b;// 1 2 ox12  ,  5 5 ox19, 66 6 ox35  ,  99 9 ox356
    for(i=1; i<position; i++)
    {
        p=p->next;
    }
    node->next=p->next;
    node->pre=p;
    p->next=node;
    if(node->next==p2){// 应对尾插法,头节点pre的处理
        p2->pre=node;
    }
    if(node->next->pre!=node){// 判断非尾插法插入
        node->next->pre=node;
    }


}
// 打印内容
void list_printf(NODE* p)
{
    NODE* pos=p->next;
    while(pos!=p)
    {
        printf("%d %d\n",pos->a,pos->b);
        pos=pos->next;
    }
}
// 清空
void list_del(NODE* p)
{
    NODE* node=p->next;
    NODE* node_tem;
    while(node!=p)// 1 2 3
    {
        node_tem=node;
        node=node->next;
        free(node_tem);
    }
    free(p);
}
// 删除指定位置的内容
void list_remove(NODE* p,int position)
{
    int i;
    NODE* pos_one=p->next;// 1 2 ox12  ,5 5 ox19  ,66 6 ox35
    for(i=1; i<position; i++)
    {
        pos_one=pos_one->next;// 即将删除位置前的一个
    }
    pos_one->pre->next=pos_one->next; //这里引用的要删除的当前元素
    pos_one->next->pre=pos_one->pre;
    free(pos_one);
}
void main()
{
    NODE* p = list_init();
    list_insert(p,1,2,1);
    list_insert(p,1115,212112,1);
    list_insert(p,1115,2121,1);
    list_remove(p,3);
    list_printf(p);
    list_del(p);
    return 0;
}



四.静态链表

优点:移动数据时只需要移动游标即可
缺点:表长固定不能变

思路:两条链路;一条是已分配路线,一条是未分配路线;其中第一位置和最后一个位置是不填充数据,第一个位置表示未分配线路起始下标,最后一个位置表示已分配路线的起始下标

1.初始化

在这里插入图片描述

// 初始化
NODE* list_init(NODE* p)// 初始时都是属于未分配这条链表
{
    int i=0;
    for(i=0;i<MAX;i++){
        (p+i)->next=i+1;
        (p+i)->data=0;
    }
    (p+MAX-1)->next=0;// 表示开始着开头元素的下标
    (p+MAX-2)->next=0;// 表示结束
}

2.插入

图解:假设已经填充了三条数据,现在已分配链是1->2,未分配链是3->4
在这里插入图片描述

图解:在第二位插入data,现在已分配链是1->3->2,未分配链是4->

在这里插入图片描述

// 按位置插入
void list_insert(NODE* head,int data,int position)// 开始增加数据形成已分配表
{


    // 初期建立的未分配表,下标0的next一直指向未分配数据
    int last = MAX-1;
    int newplace = head->next;//4
    int i;
    if(head->next) // 防止到最后一个数据为空的情况
    {
        head->next=(head+newplace)->next;
    }
    // 上面的代码是找到新空闲可分配位置
    if(newplace)
    {
        (head+newplace)->data=data;
        for(i=1; i<position; i++) //head 1 2 3
        {
            last=(head+last)->next;
        }
        (head+newplace)->next=(head+last)->next;
        (head+last)->next=newplace;
    }

}

3.删除

思路:找到被删除内容的前一个节点,想办法让其和被删除节点的下一个节点接起来;然后想办法让被删除节点连入未分配链路

// 删除指定位置的内容
void list_remove(NODE* head,int position)
{
    int i;
    int last = MAX-1;
    int old;
    for(i=1; i<position; i++) //head 1 2 3
    {
        last=(head+last)->next;
    }
    old=(head+last)->next;
    (head+last)->next=(head+old)->next;
    // 上面的代码成功断开联系,下面的代码负责刚刚释放的接入到未分配链表中去
    (head+old)->data=0;
    (head+old)->next=head->next;
    head->next=old;
}

4.打印

// 打印内容
void list_printf(NODE* p)
{
    int i;
    NODE* p1=p;
    int firstplace = (p+MAX-1)->next;
    p = p+firstplace;
    do
    {
        printf("%d\n",p->data);
        i = p->next;
        p=p1;// 让p回到起始位置
        p=p+i;
    }
    while(p->next);
    printf("%d",p->data);// 最后一位专门打印
// 下面注释的是检测数组的代码
//for(i=0;i<MAX;i++){
//    printf("%d-%d\n",p->data,p->next);
//    p++;
//}
}

5.主程序

void main()
{
    NODE p[MAX];
    list_init(p);
    list_insert(p,5,1);
    list_insert(p,69,1);
    list_insert(p,6555,1);
    list_remove(p,3);
    list_printf(p);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值