数据结构第三章----线性表

一. 顺序存储: 

定义: 用一段地址连续的存储单元依次存储元素

注: 线性表的长度<=数组长度, 线性表的第i个元素存储在数组下标为i-1的位置

优点: 不需要为表中的逻辑关系增加额外的存储空间; 快速存取表中任一位置的元素

缺点: 插入删除需要移动大量元素; 线性表长度较大时, 不好确定存储空间的容量; 造成存储空间"

"

#include<stdio.h>
#define MAXSIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;
//建立结构体
typedef struct
{
        ElemType data[MAXSIZE];
        int length;
}SqList;
//获得元素操作
Status GetElem(SqList L, int i, ElemType* e)
{
        if (L.length == 0 || i<1 || i>L.length)
                return ERROR;
        *e = L.data[i - 1];
        return OK;
}
//插入平均移动次数: (n-1)/2
Status ListTnsert(SqList* L, int i, ElemType e)
{
        int k;
        if (L->length == MAXSIZE)
                return ERROR;
        if (i<1 || i>L->length + 1)
                return ERROR;
        if (i <= L->length)
        {
                for (k = L->length - 1; k >= i - 1; i--)
                        L->data[k + 1] = L->data[k];
        }
        L->data[i - 1] = e;
        L->length++;
        return OK;
}
//删除
Status ListDelete(SqList* L, int i, ElemType* e)
{
        int k;
        if (L->length == 0)
                return ERROR;
        if (i<1 || i>L->length)
                return ERROR;
        *e = L->data[i - 1];
        if (i < L->length)
        {
                for (k = i; k > L->length; k++)
                        L->data[k - 1] = L->data[k];
        }
        L->length--;
        return OK;
}

二. 链式存储: 

1. 特点: 用一组任意的存储单元存储线性表的数据元素, 数据元素可以存在内存未被占用的任意位置,  每个结点存储本身信息的数据域, 和存放后继结点地址的指针域组成

2. 头结点与头指针的区别: 头指针: 指向第一个结点的指针, 无论链表是否为空, 头指针均不为空, 是链表的必要元素. 头结点: 为了操作方便设立, 数据域一般无意义, 不一定是链表必要元素.

3. srand(): 初始化随机种子, 为了防止随机数重复, 常使用系统时间来初始化, srand((unsigned)time(NULL)), 包含在头函数<stdlib.h>和<time.h>中, 经常和rand()函数搭配在一起

平时, 我们只用rand()函数就能够满足得到随机数的需求,但实际上,计算机产生的是伪随机数,伪随机数是计算机中已经编好的无规则排序的数字,它们的排序是没有规律的,并将它们平均分成N份,rand函数只是从这里面的数字中随机抽取一个,所以经过一个周期,获得的随机数是重复的。当srand() 的参数固定时, rand()获得的数也是固定的, 如果想在一个程序中生成随机序列, 需要至多在生成随机数之前设置一次随机种子.

4. 单链表结构与顺序存储结构的优缺点:

单链表: 任意的存储单元, 查找时间O(n), 插入删除时间O(1), 不需要分配存储空间 , 对插入或删除数据越频繁的操作, 单链表的效率优势更明显

顺序: 连续的存储单元, 查找时间O(1), 插入删除时间O(n), 需要分配存储空间, 分小了上溢, 分大了浪费

//单链表的读取
Status GetElem(LinkList L, int i, ElemType* e)
{
        int j = 1;
        LinkList p;
        p = L->next;
        while (p && j < i)
        {
                p = p->next;
                j++;
        }
        if (j > i || !p)
                return ERROR;
        *e = p->data;
}

//单链表的插入
Status Insert(LinkList *L, int i, ElemType e)
{
        int j = 1;
        LinkList p, s;
        p = *L;
        while (j < i && p)
        {
                p = p->next;
                j++;
        }
        if (j > i || !p)
                return ERROR;
        s = (LinkList)malloc(sizeof(Node));
        s->data = e;
        s->next = p->next;
        p->next = s;
        return OK;
}
//单链表的删除
Status Delete(LinkList* L, int i, ElemType* e)
{
        int j = 1;
        LinkList p, q;
        p = *L;
        while (p->next && j < i)
        {
                p = p->next;
                j++;
        }
        if (!(p->next) || j > i)
                return ERROR;
        q = p->next;
        p->next = q->next;
        *e = q->data;
        free(q);
        return OK;
}
//单链表的整表创建 头插
void CreataListHead(LinkList* L, int n)
{
        LinkList p;
        srand(time(0)); //初始化随机种子
        *L = (LinkList)malloc(sizeof(Node));
        (*L)->next = NULL;
        for (int i = 0; i < n; i++)
        {
                p = (LinkList)malloc(sizeof(Node));
                p->data = rand() % 100 + 1; //随机生成100以内的数字
                p->next = (*L)->next;
                (*L)->next = p; //插入到表头
        }
}
//尾插
void CreateListRear(LinkList* L, int n)
{
        LinkList p, r;
        srand(time(0));
        *L = (LinkList)malloc(sizeof(Node));
        r = *L;
        for (int i = 0; i < n; i++)
        {
                p = (LinkList)malloc(sizeof(Node));
                p->data = rand() % 100 + 1;
                r->next = p;
                r = p;
        }
        r->next = NULL;
}

//单链表的整表删除
Status ClearList(LinkList* L)
{
        LinkList p, q;
        p = (*L)->next; //p指向第一个结点
        while (p)
        {
                q = p->next;
                free(p);
                p = q;
        }
        (*L)->next = NULL; //头结点的指针域为空
        return OK;
}

5. 静态链表(游标实现法):

(1) 对于没有指针的, 用数组来代替指针

(2) Cur: 数组第一个元素的cur来存放备用链表第一个结点的下标, 数组最后一个元素的cur存第一个插入元素的下标, 相当于头结点

 (3) 插入: 

为辨明数组中哪些分量未被使用, 方法是将所有未被使用的以及被删除的分量用游标链成一个备用的链表, 插入时从备用链表上取得第一个结点作为新结点. 插入的思想就是改变游标, 不需要移动大量元素

书上的代码有点问题, 不允许使用返回数组的函数, 静态链表是为了给没有指针的语言使用, 重点在于理解思想

//静态链表
#define MAXSIZE 1000
typedef struct
{
        ElemType data;
        int cur; //游标
}Component,StatickList[MAXSIZE];
Status InitList(StatickList space)
{
        for (int i = 0; i < MAXSIZE - 1; i++)
                space[i].cur = i + 1;
        space[MAXSIZE - 1].cur = 0;
        return OK;
}
int Malloc_SLL(StatickList space)
{
        int i = space[0].cur;
        if (space[0].cur)
                space[0].cur = space[i].cur;
        return i;
}
StatickList ListInsert(StatickList L, int i, ElemType e)
{
        int k = MAXSIZE - 1;
        if (i<1 || i>ListLength(L) + 1)
                return ERROR;
        int j = Malloc_SLL(L);
        if (j)
        {
                L[j].data = e;
                for (int l = 1; l <= i - 1; l++)
                        int k = L[k].cur;
                L[j].cur = L[k].cur;
                L[k].cur = j;
                return OK;
        }
        return ERROR;
}

6. 循环链表

单链表: 每个结点只存储了向后的指针, 到了尾标志就停止了向后链的操作, 每次只能从头开始遍历

循环链表: 将单链表终端结点的指针端由空指针改为指向头结点, 形成一个环. 不一定有头结点

主要区别是: 循环的判断条件 单链表: p->next 是否为空, 循环: p->next 是否等于头结点

循环链表查找开始结点与终端结点时间复杂度都为O(1)

合并两个循环链表:

p = rearA->next;
rearA->next = rearB->next->next;
rear->next = p;
free(p);

7.双向链表: 

(1) 在单链表的每个结点中, 再设置一个指向其前驱结点的指针域, 每个结点都有两个指针域, 一个指向直接后继, 一个指向直接前驱

(2) p->next->prior == p == p->prior->next

(3) 在插入和删除时, 需更改两个指针变量: 前驱 后继

(4) 插入s结点: s->prior=p; s->next=p->next->prior; p->next->prior=s; p->next=s;

(5) 删除p结点: p->prior->next=p->next; p->next->prior=p->prior; free(p);

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值