C语言-数据结构(一)

数据结构笔记

一、 顺序表(一)

1、静态分配顺序表

#include <stdio.h>

#define MaxSize 10 //定义最大长度
typedef struct {
    int data[MaxSize]; //用静态数组存放数据元素
    int length; //顺序表的当前长度
}SqList; //顺序表的类型定义(静态分配方式)

void InitList(SqList *L) {
    for(int i = 0; i < MaxSize; i++)
        L->data[i] = 0; //将所有数据元素设置为默认初始值,不初始化会有脏数据
    L->length = 0; //顺序表初始长度为0
}

void GetElem(SqList L, int i) {
    for (int j = 0; j < i; ++j) {
        printf("data[%d] = %d\n", j, L.data[j]);
    }
}

int main() {
    SqList L; //声明一个顺序表
    InitList(&L); //初始化顺序表
    GetElem(L, MaxSize);
    
    return 0;
}

2、动态分配顺序表

#include <stdlib.h>
#define InitSize 10
typedef struct {
    int *data; //动态分配数组的指针
    int MaxSize; //顺手表的最大容量
    int length; //顺序表的当前长度
}SeqList; //顺序表的类型定义(动态分配方式)
//C语言动态申请和释放内存空间
//L.data = (ElemType *)malloc(sizeof(ElemType) * InitSize);
//malloc函数返回一个指针,需要强制转型为指定的数据元素类型指针

void InitList(SeqList *L){
    L->data = (int *) malloc (InitSize * sizeof(int));
    L->length = 0;
    L->MaxSize = InitSize;
}

void IncreaseSize(SeqList *L, int len){
    int *p = L->data; //用p指针指向旧顺序表的第一个元素地址,这一步相当于把旧顺序表重新起名为p了,所以最后释放的旧表就是p了
    L->data = (int *) malloc((L->MaxSize + len) * sizeof(int)); //旧顺序表的第一个元素指向一个新申请的顺序表的第一个元素地址
    for (int i = 0; i < L->length; ++i) {
        L->data[i] = p[i]; //使新顺序表的元素等于旧顺序表的元素,下标一一对应,最后新的顺序表会空出五个空间
    }
    L->MaxSize = L->MaxSize + len; //顺序表最大长度增加len个
    free(p); //释放原来的内存空间
}
int main() {
    SeqList L;
    InitList(&L);
    IncreaseSize(&L, 5);
    return 0;
}

3、顺序表的特点:

  1. 随机访问,即可以在O(1)时间内找到第i个元素
  2. 存储密度高,每个节点只存储数据元素
  3. 拓展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高)
  4. 插入、删除操作不方便,需要移动大量元素

4、顺序表的插入

#define MaxSize 10
typedef struct {
    int data[MaxSize];
    int length;
}SqList;

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

bool ListInsert(SqList *L, int i, int e){
    if(i < 1 || i > L->length+1) //判断i的范围是否有效
        return false;
    if(L->length >= MaxSize) //当前存储空间已满,不能插入
        return false;
    for (int j = L->length; j >= i ; --j) { //将第i个元素及之后的元素后移
        L->data[j] = L->data[j-1];
    }
    L->data[i-1] = e; //在位置i处放入e,传入的i是位序,数组的下标从0开始,所以位序i对应的下标是i-1
    L->length++; //长度加1
    return true;
}

int main(){
    SqList L;
    InitList(&L);
    ListInsert(&L, 1, 1); //在第一个位置插入元素1
    ListInsert(&L, 2, 2);
    for(int i = 0; i < L.length; i++) {
        printf("data[%d] = %d\n", i, L->data[i]);
    }
}

5、顺序表的删除

#define MaxSize 10
typedef struct {
    int data[MaxSize];
    int length;
}SqList;

void InitList(SqList *L) {
    for(int i = 0; i < MaxSize; i++)
        L->data[i] = i;
    L->length = 10;
}

bool ListDelete(SqList *L, int i, int *e){
    if(i<1 || i>L->length) //判断i的范围是否有效
        return false;
    *e = L->data[i-1]; //将被删除的元素赋值给e
    for (int j = i; j < L->length; ++j) { //将第i个位置后的元素前移
        L->data[j-1] = L->data[j];
    }
    L->length--; //线性表长度减1
    return true;
}

int main(){
    SqList  L;
    InitList(&L);
    int e = -1;
    if(ListDelete(&L, 3, &e))
        printf("已删除第三个元素,删除元素为%d\n", e);
    else
        printf("位序i不合法,删除失败\n");
}

6、顺序表的查找
按位查找:

#define MaxSize 10
typedef struct {
    int data[MaxSize];
    int length;
}SqList;

void InitList(SqList *L) {
    for(int i = 0; i < MaxSize; i++)
        L->data[i] = i;
    L->length = 10;
}

int GetElem(SqList L, int i){
    return L.data[i-1];
}

int main(){
    int data = 0;
    SqList L;
    InitList(&L);
    data = GetElem(L, 2);
    printf("data = %d\n", data);
}

按值查找:

#define InitSize 10
typedef struct {
    int *data; //动态分配数组的指针
    int MaxSize; //顺手表的最大容量
    int length; //顺序表的当前长度
}SeqList; //顺序表的类型定义(动态分配方式)

void InitList(SeqList *L) {
    for(int i = 0; i < InitSize; i++)
        L->data[i] = i;
    L->length = 10;
}

int LocateElem(SeqList L, int e){
    for (int i = 0; i < L.length; ++i) {
        if(L.data[i] == e)
            return i+1; //数组下标为i的元素值等于e,返回其位序i+1
    }
    return 0; //退出循环,说明查找失败
}

int main(){
    int result = 0;
    SeqList L;
    InitList(&L);
    result = LocateElem(L, 3);
    printf("result = %d\n", result);
}

二、单链表

1、不带头结点的单链表

typedef struct LNode{ //定义单链表结点类型
    int data; //每个结点存放一个数据元素
    struct LNode *next; //每个结点都有一个指向下一个结点的指针
}LNode, *LinkList; //使用typedef重命名struct LNode为LNode,重命名struct LNode *LinkList为*LinkList, *LinkList是用来指向单链表第一个结点的指针
//LinkList等价于LNode *,前者强调这是一个链表,后者强调这是一个结点
//初始化一个没有头结点的空链表
bool InitList(LinkList *L){
    L = NULL; //空表,暂时还没有任何结点
    return true;
}
//判断一个单链表是否为空
//bool Empty(LinkList L){
//    if(L == NULL)
//        return true;
//    else
//        return false;
//}

bool Empty(LinkList L){
    return (L == NULL);
}

void main(){
    LinkList L; //声明一个指向单链表的指针,即头指针
    InitList(L);
}

2、带头结点的单链表

typedef struct LNode{ //定义单链表结点类型
    int data; //每个结点存放一个数据元素
    struct LNode *next; //每个结点都有一个指向下一个结点的指针
}LNode, *LinkList;

bool InitList(LinkList L){ //LinkList类型是指针类型
    L = (LNode *) malloc(sizeof(LNode)); //分配一个头结点,返回头结点的地址给指针L,可以把L就当做头指针理解
    if(L == NULL) //内存不足,分配失败
        return false;
    L->next = NULL; //分配成功,头结点的next指针之后没有其他结点
    return true;
}

//判断一个带头结点的单链表是否为空
//bool Empty(LinkList L){
//    if(L->next == NULL)
//        return true;
//    else
//        return false;
//}

int main(){
    LinkList L; //声明一个指向单链表的指针,即头指针
    InitList(L); //初始化单链表,即增加一个头结点,初始化完成之后,头指针指向头结点
}

3、单链表插入
按位序插入

typedef struct LNode{ //定义单链表结点类型
    int data; //每个结点存放一个数据元素
    struct LNode *next; //每个结点都有一个指向下一个结点的指针
}LNode, *LinkList;

bool InsertNode(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;
}

LinkList InitList(){ //LinkList类型是指针类型
    LinkList L; //声明一个单链表头指针
    L = (LNode *) malloc(sizeof(LNode)); //分配一个头结点,返回头结点的地址给指针L,可以把L就当做头指针理解
    if(L == NULL) //内存不足,分配失败
        return false;
    L->next = NULL; //分配成功,头结点的next指针之后没有其他结点
    return L; //返回单链表的头指针,此时单链表里面已经有一个头指针和一个头结点
}

bool ListInsert(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;
//    return true;
//    }
    LNode *p; //指针p指向当前扫描到的结点
    int j = 0; //当前p指向的是第几个结点
    p = L; //一开始p会指向头结点,头结点是第0个结点
    while (p != NULL && j < i-1){ //循环找到第i-1个结点
        p = p->next;
        j++;
    }
//    if(p == NULL) //i值不合法
//        return false;
//    LNode *s = (LNode *) malloc(sizeof(LNode));
//    s->data = e;
//    s->next = p->next; //先把新结点的next指针指向下一个结点
//    p->next = s; //再把前一个结点的next指针指向新结点
//    return true;
    return InsertNode(p, e); //使用后插入函数代替以上代码
}

void InsertNextNode(LinkList L, int x, int y){ //在单链表值为x的结点后面插入一个值为3的结点
    LinkList pre, p, s;
    pre = L;
    p = L->next;
    while (p && p->data!=x){ //找到值为x的结点,获取它得next指针
        pre = p;
        p = p->next;
    }
    if(p){
        s = (LNode *) malloc (sizeof(LNode));
        s->data = y;
        s->next = p;
        pre->next = s;
    }
}

bool InsertPriorNode(LNode *p, int e){ //在给定结点前加一个结点,其实就是把新结点放到后面,再交换两个结点的值
    if(p==NULL)
        return false;
    LNode *s = (LNode *) malloc(sizeof(LNode));
    if(s==NULL)
        return false;
    s->next=p->next;
    p->next=s; //新结点s放到p后面
    s->data=p->data;
    p->data=e; //p和s值互换
    return true;
}

int main(){
    LNode  *p;
    LinkList L; //新建一个单链表,头指针为L
    L = InitList(); //初始化新建的单链表,此时单链表里面就有一个头指针指向头结点
    ListInsert(L, 1, 0); //在单链表L的第一个位置插入元素0,此时单链表里面就有一个头指针指向头结点,头结点的next指针指向插入的元素0这个结点
    ListInsert(L, 2, 1);
    ListInsert(L, 3, 2);
    InsertNextNode(L, 1, 3); //在单链表值为1的结点后面插入一个值为3的结点
    for(p = L->next; p!=NULL; p=p->next){
        printf("%d ", p->data);
    }
}

4、单链表删除

typedef struct LNode{ //定义单链表结点类型
    int data; //每个结点存放一个数据元素
    struct LNode *next; //每个结点都有一个指向下一个结点的指针
}LNode, *LinkList;

LinkList InitList(){ //LinkList类型是指针类型
    LinkList L; //声明一个单链表头指针
    L = (LNode *) malloc(sizeof(LNode)); //分配一个头结点,返回头结点的地址给指针L,可以把L就当做头指针理解
    if(L == NULL) //内存不足,分配失败
        return false;
    L->next = NULL; //分配成功,头结点的next指针之后没有其他结点
    return L;
}

bool ListInsert(LinkList L, int i, int e){
    if(i < 1)
        return false;
    LNode *p; //指针p指向当前扫描到的结点
    int j = 0; //当前p指向的是第几个结点
    p = L; //p指向头结点,头结点是第0个结点
    while (p != NULL && j < i-1){ //循环找到第i-1个结点
        p = p->next;
        j++;
    }
 //    if(p==NULL)
//        return false;
//    if(p->next == NULL) //第i-1个结点之后已无其他结点
//        return false;
//    LNode *q = p->next; //令q指向被删除的结点
//    *e = q->data; //用e返回元素的值
//    p->next = q->next; //将*q结点从单链表中断开
//    free(q); //释放结点的存储空间
//    return true;
	return InsertNextNode(p, e); //以上代码可以封装为InsertNextNode函数
}

bool ListDelete(LinkList L, int i, int *e){
    if(i < 1)
        return false;
//    LNode *p; //指针p指向当前扫描到的结点
//    int j = 0; //当前p指向的是第几个结点
//    p = L; //p指向头结点,头结点是第0个结点
//    while (p != NULL && j < i-1){ //循环找到第i-1个结点
//        p = p->next;
//        j++;
//    }
	LNode *p = GetElem(L, i-1); //将以上代码封装为GetElem查找函数
    if(p==NULL)
        return false;
    if(p->next == NULL) //第i-1个结点之后已无其他结点
        return false;
    LNode *q = p->next; //令q指向被删除的结点
    *e = q->data; //用e返回元素的值
    p->next = q->next; //将*q结点从单链表中断开
    free(q); //释放结点的存储空间
    return true;
}

bool DeleteNode(LNode *p){ //删除给定结点的后一个结点
    if(p==NULL)
        return false;
    LNode *q = p->next; //令q指向*p的后继结点
    p->data = p->next->data; //和后继结点交换数据域,但如果传入的结点是单链表的最后一个结点,这行就会报错,因为p->next为NULL
    p->next = q->next; //将*q结点从单链表中断开
    free(q); //释放后继结点的存储空间
    return true;
}

int main(){
    int *e;
    LNode  *p;
    LinkList L;
    L = InitList();
    ListInsert(L, 1, 0);
    ListInsert(L, 2, 1);
    ListInsert(L, 3, 2);
    ListInsert(L, 4, 3);
    ListDelete(L, 2, &e);
    for(p = L->next; p!=NULL; p=p->next){
        printf("%d ", p->data);
    }
    printf("\n");
    printf("%d\n", e);
}

5、单链表的查找(带头结点)
按位查找

LNode * GetElem(LinkList L, int i){
    if(i<0)
        return NULL;
    LNode *p; //指针p指向当前扫描到的结点
    int j = 0; //当前p指向的是第几个结点
    p = L; //L指向头结点,头结点是第0个结点
    while (p!=NULL && j<i){ //循环找到第i个结点
        p=p->next;
        j++;
    }
    return p;
}

按值查找

LNode * LocateElem(LinkList L, int e){
    LNode *p = L->next;
    while (p != NULL && p->data != e) //从第1个结点开始查找数据域为e的结点
        p = p->next;
    return p; //找到后返回该结点指针,否则返回NULL
}

求单链表的长度

int ListLength(LinkList L){
    int len = 0;
    LNode *p = L;
    while (p->next != NULL){
        p = p->next;
        len++;
    }
    return len;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值