数据结构_线性结构_数组+链表

数据的存储结构

  1. 线性结构

连续存储[数组]

离散存储[链表]

线性结构的应用1--栈

线性结构的应用2--队列

  1. 非线性结构

线性结构

把所有的节点用一根直线穿起来

  1. 连续存储[数组]

  • 定义:元素类型相同,所占内存大小相等。

  • 优缺点:存取的速度很快,事先必须知道数组的长度,插入删除元素很慢,空间通常有限制,需要大块连续的内存块。

示例:


#include <stdio.h>
#include <malloc.h>    //包含了malloc函数
#include <stdlib.h>    //包含了exit函数

//定义了一个数据类型, 该数据类型的名字叫做struct Arr, 该数据类型含有三个成员, 分别是pBase, len, cnt
struct Arr
{
    int* pBase; //存储的是数组第一个元素的地址
    int len; //数组所能容纳的最大元素的个数
    int cnt; //当前数组有效元素的个数
};

void init_arr(struct Arr* pArr, int length); //分号不能省
bool append_arr(struct Arr* pArr, int val); //追加
bool insert_arr(struct Arr* pArr, int pos, int val); //pos的值从1开始
bool delete_arr(struct Arr* pArr, int pos, int* pVal);
bool is_empty(struct Arr* pArr);
bool is_full(struct Arr* pArr);
void sort_arr(struct Arr* pArr);
void show_arr(struct Arr* pArr);
void inverse_arr(struct Arr* pArr);

int main(void)
{
    struct Arr arr;
    int val;

    init_arr(&arr, 6);
    show_arr(&arr);
    append_arr(&arr, 1);
    append_arr(&arr, 7);
    append_arr(&arr, 3);
    insert_arr(&arr, 1, 2);
    if (delete_arr(&arr, 2, &val))
    {
        printf("删除成功1\n");
        printf("您删除的元素是: %d\n", val);
    }
    else
    {
        printf("删除失败!\n");
    }
    show_arr(&arr); //显示追加和删除之后的内容
    inverse_arr(&arr);
    show_arr(&arr); //显示倒置后的内容
    sort_arr(&arr);
    show_arr(&arr); //显示排序后的内容

    return 0;
}

void init_arr(struct Arr* pArr, int length)
{
    pArr->pBase = (int*)malloc(sizeof(int) * length);
    if (NULL == pArr->pBase)
    {
        printf("动态内存分配失败!\n");
        exit(-1); //终止整个程序
    }
    else
    {
        pArr->len = length;
        pArr->cnt = 0;
    }
    return;
}

bool is_empty(struct Arr* pArr)
{
    if (0 == pArr->cnt)
        return true;
    else
        return false;
}

bool is_full(struct Arr* pArr)
{
    if (pArr->cnt == pArr->len)
        return true;
    else
        return false;
}

void show_arr(struct Arr* pArr)
{
    if (is_empty(pArr))
    {
        printf("数组为空!\n");
    }
    else
    {
        for (int i = 0; i < pArr->cnt; ++i)
            printf("%d    ", pArr->pBase[i]); //int *
        printf("\n");
    }
}

bool append_arr(struct Arr* pArr, int val)
{
    //满是返回false
    if (is_full(pArr))
        return false;
    //不满时追加
    else
    {
        pArr->pBase[pArr->cnt] = val;
        pArr->cnt++;
        return true;
    }
}

bool insert_arr(struct Arr* pArr, int pos, int val)
{
    if (is_full(pArr))
        return false;

    if (pos<1 || pos>pArr->cnt + 1)
        return false;

    for (int i = pArr->cnt - 1; i >= pos - 1; --i)
    {
        pArr->pBase[i + 1] = pArr->pBase[i];
    }
    pArr->pBase[pos - 1] = val;
    pArr->cnt++;
    return true;
}

bool delete_arr(struct Arr* pArr, int pos, int* pVal)
{
    if (is_empty(pArr))
        return false;

    if (pos<1 || pos>pArr->cnt)
        return false;
    *pVal = pArr->pBase[pos - 1];
    for (int i = pos; i < pArr->cnt; ++i)
    {
        pArr->pBase[i - 1] = pArr->pBase[i];
    }
    pArr->cnt--;
    return true;
}

void inverse_arr(struct Arr* pArr)
{
    int i = 0;
    int j = pArr->cnt - 1;
    int t;

    while (i < j)
    {
        t = pArr->pBase[i];
        pArr->pBase[i] = pArr->pBase[j];
        pArr->pBase[j] = t;
        ++i;
        --j;
    }
    return;
}

void sort_arr(struct Arr* pArr)
{
    int i, j, t;
    for (i = 0; i < pArr->cnt; ++i)
    {
        for (j = i + 1; j < pArr->cnt; ++j)
        {
            if (pArr->pBase[i] > pArr->pBase[j])
            {
                t = pArr->pBase[i];
                pArr->pBase[i] = pArr->pBase[j];
                pArr->pBase[j] = t;
            }
        }
    }
}
  1. 离散存储[链表]

  • 优缺点:空间没有限制,插入删除元素很快,存取的速度很慢。

  • typedef

示例1:


#include <stdio.h>

typedef int ZHANGSAN; //为int再重新多取一个名字,ZHANGSAN等价于int
typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}ST;

int main(void)
{
    //struct Student st;
    //struct Student *ps = &st;
    int i = 10; //等价于ZHANGSAN i = 10;
    ZHANGSAN j = 20;
    printf("%d\n", j);

    ST st2;
    st2.sid = 200;
    printf("%d\n", st2.sid);

    return 0;
}

示例2:


#include <stdio.h>

typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}*PST; //PST等价于struct Student *

int main(void)
{
    struct Student st;
    PST ps = &st;
    ps->sid = 99;
    printf("%d\n", ps->sid);

    return 0;
}

示例3:


#include <stdio.h>

typedef struct Student
{
    int sid;
    char name[100];
    char sex;
}*PSTU, STU; //PST等价于struct Student *, STU等价于struct Student

int main(void)
{
    STU st; //struct Student st;
    PSTU ps = &st; //struct Student *ps = &st;
    ps->sid = 99;
    printf("%d\n", ps->sid);

    return 0;
}
  • 链表的定义n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后驱节点,首节点没有前驱节点,尾节点没有后驱节点。

  • 专业术语:

  • 首节点:第一个有效节点

  • 尾节点:最后一个有效节点

  • 头节点:第一个有效节点之前的那个节点,头节点并不存放有效数据,加头节点的目的主要是为了方便对链表的操作。

  • 头指针:指向头节点的指针变量

  • 尾指针:指向尾节点的指针变量

  • 如果希望通过一个函数来对链表进行处理,至少需要接受链表的哪些参数:只需要一个参数即头指针,通过头指针可以推算出链表的其他所有信息。

  • 每一个链表节点的数据类型该如何表示:数据域和指针域


typedef struct Node
{
    int data; //数据域
    struct Node* pNext; //指针域
}NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *
  • 分类:

  • 单链表:每一个节点只有一个指针域且只能指向后面的节点

  • 双链表:每一个节点有两个指针域

  • 循环链表:能通过任何一个节点找到其他所有的节点

  • 非循环链表

  • 相关算法:

  • 遍历

  • 查找

  • 清空

  • 销毁

  • 求长度

  • 排序

  • 删除节点


//删除p所指节点的后面节点

//错误示范,导致内存泄漏
p->pNext = p->pNext->pNext;
//错误示范
free(p->pNext);

//正确方法:先临时定义一个指向p后面节点的指针r
r = p->pNext;
p->pNext = r->pNext;
free(r);
  • 插入节点


//把q所指向的节点插到p所指向的节点后面

//错误示范
p->pNext = q;
q->pNext = p->pNex;

//正确方法1:先临时定义一个指向p后面节点的指针r
r = p->pNext;
p->pNext = q;
q->pNext = r;

//正确方法2
q->pNext = p->pNext;
p->pNext = q;

示例:


#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define _CRT_SECURE_NO_WARNINGS

typedef struct Node
{
    int data; //数据域
    struct Node* pNext; //指针域
}NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *

//函数声明
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
void sort_list(PNODE pHead);
bool insert_list(PNODE pHead, int pos, int val);
bool delete_list(PNODE pHead, int pos, int* pVal);

int main(void)
{
    PNODE pHead = NULL; //等价于struct Node *pHead = NULL;
    int val;

    pHead = create_list(); //create_list()功能: 创建一个非循环单链表, 并将该链表的头节点的地址赋给pHead
    traverse_list(pHead);
    int len = length_list(pHead);
    printf("链表长度是%d\n", len);
    if (is_empty(pHead))
        printf("链表为空!\n");
    else
        printf("链表不空!\n");

    sort_list(pHead);
    traverse_list(pHead);

    insert_list(pHead, 2, 3);
    traverse_list(pHead);

    if (delete_list(pHead, 1, &val))
    {
        printf("删除成功删除的元素是%d\n", val);
    }
    else
    {
        printf("删除失败!您删除的元素不存在!\n");
    }
    traverse_list(pHead);

    return 0;
}

PNODE create_list(void)
{
    int len; //用来存放有效节点的个数
    int i;
    int val; //用来临时存放用户输入的节点的值

    //分配了一个不存放有效数据的头节点
    PNODE pHead = (PNODE)malloc(sizeof(NODE));
    if (NULL == pHead)
    {
        printf("分配失败,程序终止!\n");
        exit(-1);
    }

    PNODE pTail = pHead;
    pTail->pNext = NULL;

    printf("请输入您需要生成的链表节点的个数:len = ");
    scanf("%d", &len);

    for (i = 0; i < len; ++i)
    {
        printf("请输入第%d个节点的值: ", i + 1);
        scanf("%d", &val);
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        if (NULL == pHead)
        {
            printf("分配失败,程序终止!\n");
            exit(-1);
        }
        pNew->data = val;
        pTail->pNext = pNew;
        pNew->pNext = NULL;
        pTail = pNew;
    }
    return pHead;
}

void traverse_list(PNODE pHead)
{
    PNODE p = pHead->pNext;

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

bool is_empty(PNODE pHead)
{
    if (NULL == pHead->pNext)
        return true;
    else
        return false;
}

int length_list(PNODE pHead)
{
    PNODE p = pHead->pNext;
    int len = 0;
    while (NULL != p)
    {
        ++len;
        p = p->pNext;
    }
    return len;
}

void sort_list(PNODE pHead)
{
    int i, j, t;
    int len = length_list(pHead);
    PNODE p, q;

    for (i = 0, p = pHead->pNext; i < len - 1; ++i, p = p->pNext)
    {
        for (j = i + 1, q = p->pNext; j < len; ++j, q = q->pNext)
        {
            if (p->data > q->data)
            {
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
        }
    }
    return;
}

//在pHead所指向链表的第pos个节点的前面插入一个新的节点, 该节点的值是val, 并且pos的值是从1开始的 
bool insert_list(PNODE pHead, int pos, int val)
{
    int i = 0;
    PNODE p = pHead;

    while (NULL != p && i < pos - 1)
    {
        p = p->pNext;
        ++i;
    }

    if (i > pos - 1 || NULL == p)
        return false;

    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    if (NULL == pNew)
    {
        printf("动态分配内存失败!\n");
        exit(-1);
    }
    pNew->data = val;
    PNODE q = p->pNext;
    p->pNext = pNew;
    pNew->pNext = q;

    return true;
}

bool delete_list(PNODE pHead, int pos, int* pVal)
{
    int i = 0;
    PNODE p = pHead;

    while (NULL != p && i < pos - 1)
    {
        p = p->pNext;
        ++i;
    }

    if (i > pos - 1 || NULL == p)
        return false;

    PNODE q = p->pNext;
    *pVal = q->data;

    //删除p节点后面的节点
    p->pNext = p->pNext->pNext;
    free(q);
    q = NULL;

    return true;
}

以上内容来源于小破站:郝斌数据结构入门

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值