2020/04/21被迫营业,由于对本学期第一次数据结构实验——设计实现“一元多项式计算器”,实在是没有头绪,故我打算,先从课本知识学起!!!
可能写的会比较啰嗦,因为是第一遍过嘛,细致一点总归是好的~~
线性表
抽象数据类型
先说说 数据类型 是啥?**********例如很多编程语言的整型、浮点型、字符型、、、、
那 抽象数据类型ADT 呢?**********抽取出事物具有的普遍性本质,意义在于减少设计过程和开发过程,有点像“类”一样。
一堆人排个队??上个厕所回来还知道自己在哪吗??
最简单的方法,记住你前面的或者后面的人方便你找到自己在哪…
放在线性表里就是ai-1 , ai ,ai+1,(ai的前驱和后继)
线性关系::第一个元素无前驱,最后一个元素无后继,其他元素有且只有一个前驱和后继。
元素之间:一对一的关系(没有小三)
其操作有 // 创建、重置(抹除重创)、删除(缺一个其他往前挪)、插入(大家往后挪,空出来位给新成员)…。。
InitList(*L) 初始化操作
ListEmpty(L) 是否为空表
ClearList(*L) 清空线性表
GetElem(L,i,*e) L中第i个元素返回给e
LocateElem(L, e) 查找与e等值的元素位序
ListInsert(*L,i,e) 在第i个位置插新元素e
ListDelete(*L,i,*e) 删第i个,用e返回其值
ListLength(L) 线性表的元素个数(长度)
…。。。。。。
数据结构和算法,是写给人看的,1就是1。第一个就是1,不是0.
如果操作返回值是0,那就是出错了~
求个A=A∪B
void union(list &LA, list LB)
{
//将所有在B中但是没在A中的元素都插入到A中
lan_length = ListLength(LA); lbn_length = ListLength(LB);
for(i = 1;i <= lbn_length; i++)//遍历B
{
GetElem(LB, i, e);//取B中元素
if(!LocateElem(LA,e,equal))
ListInsert(LA,++lan_length,e);//不同与A中元素,插入
}
}
线性表的顺序存储
#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;//线性表当前长度
}SqList;
//封装了一个结构,事实上就是对数组进行封装,增加了当前长度
封装需要三个属性!…存储空间的起始位置,,线性表的最大存储容量,,线性表的当前长度
区分:数组的长度是存放线性表的存储空间的总长度,通常初始化后不会改变;线性表的length是当前元素个数,可变。
基本操作的实现
- GetElem(*L, i, e)
#define OK 1
#define ERROR 0
typedef int Status;
Status GetElem(Sqlist L, int i, ElemType *e)
{
if(L.length == 0 || i < 1 || i > L.length)
{//初始条件 L已存在,1<=i<=L.length
return ERROR;
}
*e = L.data[i-1];//用e返回L中第i个数据的值
return OK;
}
- ListInsert(*L, i, e)
Status ListInsert(SqList *L,int i, ElemType e)
{
int k;
if(L->length == MAXSIZE)//顺序表满了
return ERROR;
if(i < 1 || i > L->length)//i不在范围内
return ERROR;
if(i <= L->length)//插入的数据不在表尾
{//借助 k,将要插入的数据后元素向后移动一位
for(k = L->length-1; k >= i-1; k-- )
L->data[k+1] = L->data[k];
}
L->data[i-1] = e;//插入元素(在表尾就直接附上)
L->length++;
return OK;
}
- ListDelete(*L,i,*e)
Status ListDelete(SqList *L,int i, ElemType e)
{
int k;
if(L->length == 0)//顺序表空,何谈删除?
return ERROR;
if(i < 1 || i > L->length)//i不在范围内
return ERROR;
*e = L->data[i-1];//数组从0开始的第i,链表里是i-1
if(i <= L->length)
{//向前移动1位覆盖
for(k = i; k < L->length; k++ )
L->data[k-1] = L->data[k];
}
L->length--;
return OK;
}
– 相邻两元素的存储位置也具有邻居关系,它们在内存中的位置依然是紧挨着的,中间没有间隙,当然就无法快速插入和删除啦!!倒是能快速存取表中任意位置元素~
线性表的链式存储
除了存储其本身信息之外,还需存储一个指示其直接后继的信息,这两部分信息组成数据元素ai的存储映像,即结点node(数据域和指针域)
typedef struct Node
{
ElemType date;//数据域
struct Node* Next;//指针域
}Node
typedef struct Node* LinkList;//取别名
p->data得到数据元素的值 ; p->next的值是个指针,指向下个结点的地址
在单链表中,获取第i个数据元素必须从头指针出发寻找…那这样,GetElem(L, i, *e)如何实现呢?
Status GetElem(Linklist L, int i, ElemType *e)
{
p = L->next;//声明一个结点p指向链表第一个结点
j = 1;//j从1开始计数
while(p && j<i)
{
p = p->next;//p指针后移,不断指向下一结点
++j;
}
if(!p || j>i)
return ERROR
*e = p->data;//查找成功
return OK;
}
最坏情况的时间复杂度=平均情况时间复杂度 = O(n)
单链表的插入
用不着惊动其他结点,只需要调一下两个指针的指向
s->next = p->next
p->next = s
单链表的删除
p->next = p->next->next
对于插入或删除数据越频繁的操作,单链表的效率优势越明显~~
单链表和顺序存储不太一样,它的数据不堆积,分散在内存的各个角落;所占空间的大小和位置是不需要预先分配划定的;
头插法建立单链表每次都插入表头
尾插法建立单链表每次都加入表尾
void createListHead(LinkList *L,int n)
{//头插法建立单链表
LinkList p;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
(*L)->Next = NULL;/* r = *L;指向尾部的结点*/
/*尾插法建立单链表*/
for(i = 0;i < n; i++)
{
p = (LinkList)malloc(sizeof(Node));
p->date = rand()%100 + 1;//生成1-100内的随机数
p->next =(*L)->next;
(*L)->next = p;
/* r->next =p;
r = p 动态更换尾部结点p */
}
}
静态链表
用数组描述的链表…需要用户自己实现malloc和free这两个函数
( 将所有未被使用过以及被删除的分量用游标链成一个备用的链表,
每当进行插入时便可从备用链表上取得第一个结点作为待插入的新结点;在删除时将从链表中删除下来的结点链接到备用链表上)
循环链表
表中最后一个结点的指针域指向头结点
终端结点用尾指针rear指示,查找终端结点是O(1),开始结点是rear->next->next,也是O(1)
双向链表
两个指针域,一个指向直接后继,另一指向直接前驱。
typedef struct DuLNode{
ElemType data;
struct *prior;
struct DuLNode *next;
}DuLNode,*DuLinkList;
删除?p->piror->next = p->next; . p->next->piror = p->piror; free§;//释放结点到存储池内
插入?
s->data = e ;
s->prior = p->prior; p->prior->next = s;
s->next = p; p->prior = s;
代码练习???
qiao 吧 qiao吧