数据结构 线性表部分代码(c)


文章仅个人学习数据结构的实现记录,对理论性文字很少,仅仅粘贴实现的c代码以及部分测试,其中测试结果无说明

顺序表

malloc函数分配的空间是在堆区,需要程序员自行回收(free)

  • 初始化顺序表的实现——静态结构
    这里要注意指针的使用
/*
This is a define of sequence
*/

#include<stdio.h>
#define MaxSize 10 // the max size

// Describe the sequence
typedef struct{
    int data[MaxSize]; // this are elements
    int length;// the real size
}SqList;

// Init
void InitList(SqList *L){
    for(int i=0;i<MaxSize;i++){
        L->data[i]=0;
    }
    // set the real size
    L->length=0;
}

int main(){
    SqList s;
    // &s:it is an address
    InitList(&s);
    s.data[0]=1;
    s.length++;
    for(int i=0;i<s.length;i++){
        printf("%d\n",s.data[i]);
    }
}
  • 插入——健壮性
// Insert
bool ListInsert(SqList *L,int i,int e){
    // this index is not legal;tips:i means 位序, not the index
    if((i-1)<0||(i-1)>L->length)
        return false;
    // there is no position you can insert into
    if(L->length>=MaxSize)
        return false;

    // TODO if e is not legal

    for(int j=L->length-1;j>=(i-1);j--){
        L->data[j+1]=L->data[j];
    }
    L->data[i-1]=e;
    L->length++;
    return true;
}
  • 删除
// Delete
bool ListDelete(SqList *L,int i,int *e){
    // this index is not legal;tips:i means 位序, not the index
    if((i-1)<0||(i-1)>L->length)
        return false;

    *e=L->data[i-1];
    // move the element to the next position(while index>i)
    for(int j=i-1;j<L->length;j++){
        L->data[j]=L->data[j+1];
    }
    
    L->length--;
    return true;
}
  • 按照下标查找
// find by index
int GetElem(SqList L,int i){
    if((i-1)<0||(i-1)>L.length)
        return -1;
    return L.data[i-1];
}
  • 按照元素查找
// find by element
int LocateElem(SqList L,int e){
    for(int i=0;i<L.length;i++){
        if(L.data[i]==e){
            return i+1;
        }
    }
    return -1;
}

单链表

  • 单链表的定义(是否带头结点)
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>

// linklist
// define linklist 
typedef struct LNote{
    int data;
    struct LNode *next;
}LNode,*LinkList;//重命名数据类型为LNode,并且重命名其指针为LinkList,当说明的是LNode* 的时候,表明这是一个节点,当写的是LinkList的时候,说明这是一个单链表

// init
bool InitList_withoutHead(LinkList *L){
    // NULL:avoid dirty data
    (*L)=NULL;
    return true;
}
bool InitList_withHead(LinkList *L){
    *L=(LNode *)malloc(sizeof(LNode));
    if(*L==NULL)
        return false;
    (*L)->next=NULL;
    return true;
}

// judge
bool Empty(LinkList L){
    if(L==NULL)
        return true;
    else
        return false;
}
  • 单链表的插入——按位序插入(带头结点)
// Insert
bool Listinsert_withHead(LinkList *L,int i,int e){
    // i means 位序
    if(i<1)
        return false;
    LNode *p;//指针p指向当前扫描到的节点
    int j=0;// 当前p指向的是第几个节点,这里指向的是头结点,认为是0节点
    p=(*L);
    // p->data=1111;
    // printf("%d\n",p->data);
    while(p!=NULL&&j<i-1){
        p=p->next;
        j++;
    }
    // i is not legal
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;// 这两句次序不可以改变
    p->next=s;
    return true;
}

// test
void test1(){
   LinkList L;
    // L做头结点
    InitList_withHead(&L);
    Listinsert_withHead(&L,1,11);
    Listinsert_withHead(&L,2,22);
    Listinsert_withHead(&L,3,33);
    LNode *p=L->next;
    while(p!=NULL){
        printf("%d\n",p->data);
        p=p->next;
    }
}
  • 单链表的插入——按位序插入(不带头结点)

    这个时候,当n=1的时候,需要特别使用一段代码来实现,特别麻烦

bool Listinsert_withoutHead(LinkList *L,int i,int e){
    if(i<1)
        return false;
    if(i==1){
        LNode *s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=*L;
        // 需要更改头指针,使得L(头指针)直接就是s
        *L=s;
        return true;
    }
    LNode *p;
    int j=1;//没有头结点,也就是0节点,所以当前p是从第一个节点开始的
    p=*L;
    while(p!=NULL&&j<i-1){
        p=p->next;
        j++;
    }
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
void test2(){
    LinkList L;
    InitList_withoutHead(&L);
    Listinsert_withoutHead(&L,1,1);
    Listinsert_withoutHead(&L,1,2);
    Listinsert_withoutHead(&L,1,3);
    Listinsert_withoutHead(&L,1,4);
    LNode *p=L;
    while(p!=NULL){
        printf("%d\n",p->data);
        p=p->next;
    }
}

因为不带头结点的单链表插入删除非常麻烦,所以后续的代码没有特别说明都是带头结点的。

  • 指定节点后插操作

时间复杂度为O(1)

// Insert element after designated node
bool InsertNextNode(LNode *p,int e){
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    // 内存分配失败
    if(s==NULL)
        return false;
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}

// test
void test1(){
   LinkList L;
    // L做头结点
    InitList_withHead(&L);
    Listinsert_withHead(&L,1,11);
    Listinsert_withHead(&L,2,22);
    Listinsert_withHead(&L,3,33);
    // 不可以出现两个p->next->next,不然要p=p->next干啥
    LNode *p=L->next;
    p=p->next;
    InsertNextNode(p,123);

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

对于元素的指定位置i的插入操作,只要找到第i-1个节点,就可以使用后插操作实现对指定位置i的插入操作。那么指定位置插入的代码会变为以下。

bool Listinsert_withHead(LinkList *L,int i,int e){
    // i means 位序
    if(i<1)
        return false;
    LNode *p;//指针p指向当前扫描到的节点
    int j=0;// 当前p指向的是第几个节点,这里指向的是头结点,认为是0节点
    p=(*L);
    // p->data=1111;
    // printf("%d\n",p->data);
    while(p!=NULL&&j<i-1){
        p=p->next;
        j++;
    }
    return InsertNextNode((LNode *)p,e);
    // i is not legal
    // if(p==NULL)
    //     return false;
    // LNode *s=(LNode *)malloc(sizeof(LNode));
    // s->data=e;
    // s->next=p->next;// 这两句次序不可以改变
    // p->next=s;
    // return true;
}
  • 指定结点前插

节点不能跑路,但是结点的数据可以变化,当需要指定位置前插的时候,可以考虑申请一个后继节点s放在p节点后,修改s的数据为要p的数据2,修改p的数据为要插入的数据,这样实现的时间复杂度为O(1)

bool InsertPriorNode(LNode *p,int e){
    // 时间复杂度为O(1)
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    // 内存分配失败
    if(s==NULL)
        return false;

    s->data=p->data;
    s->next=p->next;
    p->next=s;
    p->data=e;
    return true;

}

// test
void test1(){
   LinkList L;
    // L做头结点
    InitList_withHead(&L);
    Listinsert_withHead(&L,1,11);
    Listinsert_withHead(&L,2,22);
    Listinsert_withHead(&L,3,33);
    LNode *p=L->next;
    p=p->next;
    InsertPriorNode(p,123);

    while(p!=NULL){
        printf("%d\n",p->data);
        p=p->next;
    }
}
  • 头插法建立单链表——链表的逆置

头插法建立单链表的时候,读入的数据与生成的链表的元素的顺序是相反的,当数据规模是n的时候,时间复杂度为O(n)

// 头插法建立单链表
void List_HeadInsert(LinkList *L){
    LNode *s;
    int e;
    scanf("%d",&e);
    while(e!=999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=(*L)->next;
        (*L)->next=s;
        scanf("%d",&e);
    }
    // return L;
}
void test2(){
    LinkList L;
    InitList_withHead(&L);
    List_HeadInsert(&L);

    LNode *p=L->next;
    while(p!=NULL){
        printf("%d->",p->data);
        p=p->next;
    }
}
  • 尾插法建立单链表

时间复杂度和头插法一样,它生成链表的顺序和我们输入数据的顺序是一样的,尾插法需要动态维护一个尾指针,值得注意的是,如果没有把尾指针指向NULL,程序可能会出现不可预知的后果

// 尾插法建立单链表
void List_TailInsert(LinkList *L){
    int e;
    LNode *s,*r=*L;
    scanf("%d",&e);
    while(e!=999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        r->next=s;
        r=s;
        scanf("%d",&e);
    }
    // 如果这里不添加NULL,则r的后面就会随意指向一个地址,将出现大问题
    r->next=NULL;
}
// print LinkList
void printfLinkList(LinkList L){
    LNode *p=L->next;
    while(p!=NULL){
        printf("%d->",p->data);
        p=p->next;
    }
}
void test2(){
    LinkList L;
    InitList_withHead(&L);
    List_TailInsert(&L);

    printfLinkList(L);
}
  • 按照位序删除结点

首先先检验位置的合法性,查找节点的第i-1个结点,最后进行删除操作。由于查找的事件复杂度为O(n),所以整体上时间复杂度为O(n)

if(p->next==NULL)//i-1后面没结点,不需要删除
        return false;
// 按位序删除-带头结点
bool ListDelete(LinkList *L,int i,int *e){
    if(i<1)
        return false;
    LNode *p=*L;// p指向当前扫描到的结点
    int j=0;//指向p的第几个节点
    
    while(p!=NULL&&j<i-1){//循环找到第i个结点
        p=p->next;
        j++;
    }
    if(p==NULL)//i的值不合法
        return false;
    if(p->next==NULL)//i-1后面没结点,不需要删除
        return false;

    LNode *q=p->next;
    p->next=q->next;
    *e=q->data;
    free(q);
    return true;

}
  • 指定结点的删除

如果给定一个头结点,可以找到指定节点的前驱节点,但是这样的事件复杂度为O(n),可以换一种思路,首先删除给定结点的后继节点,然后把后继节点的数据修改到当前指定节点的数据,这样的时间复杂度为O(1)

//有bug的代码,当删除的是最后一个节点的时候,执行到p->data=q->data;的时候会发生空指针错误
bool ListDelete_piont(LNode *p){
    if(p==NULL)
        return false;
    LNode *q=p->next;
    p->data=q->data;
    p->next=q->next;
    free(q);
    return true;

}

但是如果删除的结点p是最后一个结点,不能直接free§,这样前驱节点指向的下一个节点会随机,会造成不可预知的后果。如果是这种情况下的删除,并且当前给出了链表的头指针,可以考虑先找到单链表的前驱节点,再删除结点。

  • 按位查找

事件复杂度为O(n),这里我们认为,头结点为0号节点

LNode *GetElem(LinkList L,int i){
    if(i<0)
        return NULL;
    LNode *p=L;
    int j=0;
    while(p!=NULL&&j<i){
        p=p->next;
        j++;
    }
    return p;
}
  • 按值查找

时间复杂度为O(n)

LNode *GetElem_element(LinkList L,int e){
    LNode *p=L->next;
    while(p!=NULL&&p->data!=e)
        p=p->next;
    return p;
}

双链表

  • 初始化双链表
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>

typedef struct DNode{
    int data;
    struct DNode *prior,*next;
}DNode,*DLinklist;

// init Double Linklist
bool InitDLinklist(DLinklist *L){
    // 分配一个头结点
    *L=(DNode *)malloc(sizeof(DNode));
    if(*L==NULL)//内存分配失败
        return false;

    (*L)->prior=NULL;
    (*L)->next=NULL;
    return true;
}
// judge double linklist is empty or not
bool Empty(DLinklist L){
    if(L->next==NULL)
        return true;
    return false;
}
  • 在p结点后面插入一个双链表——后插操作
// insert a new node after designed node-p
//如果p结点是最后一个结点,那么在执行p->next->prior=s;会爆空指针错误
bool InsertNextDNode(DNode *p,DNode *s){
    s->next=p->next;
    p->next->prior=s;
    s->prior=p;
    p->next=s;
}

更具有健壮性的代码

// insert a new node after designed node_p
bool InsertNextDNode(DNode *p,DNode *s){
    if(p==NULL||s==NULL)//非法参数
        return false;
    s->next=p->next;
    if(p->next!=NULL)//没有后继结点的情况
        (p->next)->prior=s;
    s->prior=p;
    p->next=s;
    return true;
}
void printfDLinklist(DLinklist L){
    DNode *p=L->next;
    while(p!=NULL){
        printf("%d->",p->data);
        p=p->next;
    }
}
void test1(){
    DLinklist L;
    InitDLinklist(&L);
    DNode *s=(DNode *)malloc(sizeof(DNode));
    DNode *p=(DNode *)malloc(sizeof(DNode));
    p=L;
    s->data=64;
    InsertNextDNode(p,s);
    printfDLinklist(L);
}
void main(){
    test1();
}
  • 删除结点
// delete node
bool DeleteNextLinklist(DNode *p,int *e){
    if(p==NULL||p->next==NULL)
        return false;
    DNode *q=p->next;
    *e=q->data;
    if(q->next!=NULL)
        q->next->prior=p;
    p->next=q->next;
    free(q);
    return true;
}
void test1(){
    DLinklist L;
    InitDLinklist(&L);
    DNode *s=(DNode *)malloc(sizeof(DNode));
    DNode *q=(DNode *)malloc(sizeof(DNode));
    DNode *p=(DNode *)malloc(sizeof(DNode));
    p=L;
    s->data=64;
    s->next=NULL;
    InsertNextDNode(p,s);
    q->data=63;
    q->next=NULL;
    InsertNextDNode(p,q);
    printfDLinklist(L);
    int e=0;
    DeleteNextLinklist(p,&e);
    printf("\n");
    printfDLinklist(L);
}
  • 销毁双链表
void DestoryList(DLinklist *L){
    int e=0;
    // 循环删除L的后继结点
    while((*L)->next!=NULL){
        DeleteNextLinklist(*L,&e);
    }
    free(*L);
    *L=NULL;
}

循环链表

在定义循环链表的时候,如果使用的是头指针,在查询最后一个节点的时候时间复杂度为O(n),但是如果指针L指向表尾元素,那么从尾部找到头部和直接找尾部的时间复杂度都是O(1),这对于经常要使用头尾结点的场景很友好。

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

typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;

// init
bool InitList(LinkList *L){
    *L=(LNode *)malloc(sizeof(LNode));
    if(*L==NULL)
        return false;

    (*L)->next=*L;
    return true;
}
// judge
bool Empty(LinkList L){
    if(L->next==L)
        return true;
    return false;
}

// judge whether the p node is a tail pointer
bool isTail(LinkList L,LNode *p){
    if(p->next==L)
        return true;
    return false;
}
void test1(){
    LinkList L;
    InitList(&L);
    if(Empty(L))
        printf("yes!");

    if(isTail(L,L))
        printf("is tail!");
}
void main(){
    test1();
}

循环双链表

  • init
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef struct CDNode{
    int data;
    struct CDNode *prior,*next;
}CDNode,*CDLinklist;

// init 
bool InitCDLinklist(CDLinklist *L){
    *L=(CDNode *)malloc(sizeof(CDNode));
    if(*L==NULL)
        return false;

    (*L)->prior=*L;
    (*L)->next=*L;
    return true;
}
// judge whether the cdlinklist is empty
bool Empty(CDLinklist L){
    if(L->next==L)
        return true;
    return false;
}
// judge whether the p node is tail pointer
bool isTail(CDLinklist L,CDNode *p){
    if(p->next==L)
        return true;
    return false;
}
void test1(){
    CDLinklist L;
    InitCDLinklist(&L);
    if(isTail(L,L))
        printf("init completely!");

}
void main(){
    test1();
}
  • insert

特别要注意的是,这里的输出的判断是p的下一个节点是否是L,因为这个是循环双链表

// insert a new node s after p node
bool InsertNextCDNode(CDNode *p,CDNode *s){
    s->next=p->next;
    p->next->prior=s;
    s->prior=p;
    p->next=s;
}
void printfCDLinklist(CDLinklist L){
    CDNode *p=L->next;
    while(p!=L){
        printf("%d->",p->data);
        p=p->next;
    }
}
void test1(){
    CDLinklist L;
    InitCDLinklist(&L);
    CDNode *s=(CDNode *)malloc(sizeof(CDNode));
    s->data=111;
    s->next=NULL;
    s->prior=NULL;
    CDNode *x=(CDNode *)malloc(sizeof(CDNode));
    x->data=222;
    x->next=NULL;
    x->prior=NULL;
    InsertNextCDNode(L,s);
    InsertNextCDNode(s,x);
    printfCDLinklist(L);
}
void main(){
    test1();
}

静态链表

静态链表首先要分配一整片连续的内存空间,各个结点集中安置。静态链表和单链表类似,包含数据元素和指针元素(游标),但是这里的指针元素指向的是下一个节点的地址(数组下标)。并且0结点充当“头结点”。静态链表对于一些不支持指针的高级语言来讲设计是很巧妙的。

  • 定义一个结构体
#include<stdlib.h>
#include<stdbool.h>
#include<stdio.h>

#define MAXSIZE 10//这里是不允许有分号结束符的
typedef struct{
    int data;
    int next;//下一个元素的数组下标
}SLinklist;


void test(){
    SLinklist s[MAXSIZE];
    printf("%d\n",sizeof(s));
}
void main(){
    test();
}
  • init
// init
bool initSLinklist(SLinklist s[MAXSIZE]){
    // 值得注意的是,在c的标准中是没有定义数组的长度进行定义的,但是在定义一个数组的时候,要求数组中括号中的数必须是常量和或者常量表达式,所以这个常量是可以作为长度传入的,但是这一操作却是在java中被允许的
    if(MAXSIZE==0)
        return false;
    s[0].next=-1;
    return true;
}
void test(){
    SLinklist s[MAXSIZE];
    if(initSLinklist(s))
        printf("yes!\n");
    printf("%d\n",sizeof(s));
}
void main(){
    test();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是 C 语言实现线性表代码示例: ```c #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 // 定义线性表的最大长度 typedef struct { int data[MAX_SIZE]; // 存储数据的数组 int length; // 当前线性表的长度 } List; // 初始化线性表 void initList(List *list) { list->length = 0; // 线性表长度为0 } // 获取线性表长度 int getListLength(List *list) { return list->length; } // 在线性表末尾插入元素 int insertList(List *list, int element) { if (list->length >= MAX_SIZE) { // 线性表已满 return 0; } list->data[list->length++] = element; return 1; } // 删除元素 int deleteList(List *list, int element) { int i, j; for (i = 0; i < list->length; i++) { if (list->data[i] == element) { // 找到要删除的元素 for (j = i; j < list->length - 1; j++) { list->data[j] = list->data[j + 1]; // 将后面的元素向前移动 } list->length--; // 线性表长度减1 return 1; } } return 0; // 没有找到要删除的元素 } // 获取某个位置上的元素 int getElement(List *list, int index) { if (index < 0 || index >= list->length) { return -1; // 越界,返回-1 } return list->data[index]; } // 查找元素 int searchList(List *list, int element) { int i; for (i = 0; i < list->length; i++) { if (list->data[i] == element) { return i; // 找到元素,返回下标 } } return -1; // 没有找到元素,返回-1 } int main() { List list; initList(&list); // 初始化线性表 // 在线性表末尾插入元素 insertList(&list, 1); insertList(&list, 2); insertList(&list, 3); insertList(&list, 4); insertList(&list, 5); // 删除元素 deleteList(&list, 3); // 获取某个位置上的元素 printf("Element at index 2: %d\n", getElement(&list, 2)); // 查找元素 printf("Index of element 4: %d\n", searchList(&list, 4)); return 0; } ``` 以上代码实现了线性表的基本操作,包括初始化、获取长度、插入元素、删除元素、获取元素、查找元素。注意:本代码仅为示例代码,实际使用中还需根据具体需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值