/*
ADT 线性表list
Data
满足特征的数据,每个元素类型为DataType
Opration
InitList(L): 初始化操作,建立一个空的线性表
ListEmpty(L): 若线性表为空,返回ture,否则false
ClearList(L): 清空线性表
GetElem(L,i,*e):将L中第i个元素返回给e
LoacateElem(L,e):在L中查找与e相等元素,返回位置,没有返回0
ListInsert(*L,i,e):在L的第i个元素位置插入e
ListDelete(*L,i,*e):在L的第i个元素位置删除,并用e返回
ListLength(L) :返回线性表L的元素个数
endADT
*/
1.线性表的顺序储存结构实现
顺序结构需要三个条件;指定一个存储空间Maxsize;其次要知道存储的位置data[];最后要知道长度length
//顺序存储的结构实现
#include <stdio.h>
#include <Windows.h>
#define MAXSISZ 20
typedef int ElemType;
typedef struct
{
ElemType data[MAXSISZ];
int length;
}SqList;
2.线性表顺序存储的操作
查找、插入、删除、取值、修改
2.1 线性表顺序存储取值操作
两步即可完成:
1.判断位置是否合法
2.见数组值赋值返回
//顺序存储元素获取,从1开始
#define ERROR 0
#define OK 1
#define True 1
#define False 0
typedef int Status; //定义返回位置
Status GetElem(SqList L,int i,ElemType* e)
{
if (i>L.length ||i<1 ||L.length==0)
{
return ERROR;
}
*e =L.data[i-1];
return OK;
}
2.2线性表顺序存储插入值操作
完成步骤:
1.判断插入条件
2.从后往前移动一个位置
3.在i的位置停止,赋值
Status ListInsert(SqList *L,int i,ElemType e)
{
if (L->length==MAXSISZ)
return ERROR;
if (i>L->length+1 || i<0) //i超出范围
return ERROR;
if (i<=L->length)
{
for (int j=L->length;j>=i-1;j--)
L->data[j+1]=L->data[j];
L->length = L->length+1;
L->data[i-1]=e;
return OK;
}
}
2.3线性表顺序存储删除元素操作
步骤:
1.检查合法性,非法抛出异常(i>length;i<0;)
2.从i开始前移一个位置
3.表长减1
Status ListDelete(SqList *L,int i, ElemType *e)
{
if (i<0)
return ERROR;
if (i>L->length)
return ERROR;
if (i>0 && i<=L->length)
{
*e=L->data[i-1];
for (int j=i-1;j<=L->length;j++)
{
L->data[j]=L->data[j+1];
}
return OK;
}
}
3.线性表的链式存储结构
3.1 链表的定义与结构
定义:像铁链一样的结构的线性表,铁链的每个环称为结点
结点(node):铁链的一环,可以分为两部分:一部分用来扩张长度;一部分用来连接下一环;因此:结点是由数据域和指针域构成;铁链的头可能系的绳子:头指针
3.2 链表的分类
结点的定义:数据元素由数据域和指针域构成
typedef struct Node
{
ElemType data; //数据域
struct Node *next; //指针域
}Node;
typedef struct Node *LinkList; //定义了一个头指针,指向node
3.2.1.单链表
a. 单链表的读取:
步骤:
1.从头开始,不断读取下一个,直至返回
2.未读取到时返回空
Status GetElemLinkList(LinkList L,int i,ElemType *e)
{
LinkList p; //声明一个指针
p=L->next; //p指向l的首个指针
int j = 1; //计数
while (p && j<i)
{
p->next;
j++;
}
if(!p || j>i)
return ERROR;
*e =p->data;
return OK;
}
b. 单链表的插入:
步骤:
1.循环读取定位到位置i
2.生成一个空结点,存放e
3.指针指向e地址,e的指针指向下一结点
Status LinkListInsert(LinkList l,int i, ElemType e)
{
LinkList p,s; //声明一个指针
p=l->next; //p指向l的首结点
int j = 1; //计数器
while (p && j<i)
{
p->next;
j++;
}
if (!p ||j>i)
{
return ERROR;
}
s = (LinkList)malloc(sizeof (Node)); //生成一个新结点
s->data = e; //结点赋值
s->next=p->next; //新结点指向下一结点
p->next=s; //指向新结点
return OK;
}
c. 单链表的删除:
步骤:
1.循环读取定位到位置i
2.指针指向下下个
3.释放结点,返回e
Status LinkListDelete(LinkList l,int i ,ElemType *e)
{
LinkList p,q;
p =l->next;
int j =1;
while(p->next && j<i-1)
{
p=p->next;
j++;
}
if (!(p->next)||j>i)
return ERROR;
q=p->next; //用q指向i
p->next=q->next;//将i指向换为i-1指向
*e=q->data;
free(q);
return OK;
}
d. 单链表的创建(头插法)
定义:所谓头插法,就是指每个新结点都放在第一位
步骤:
1.产生一个空链表L
2.声明指针p和计数器l
3.l的头结点指向null
4.循环插入新结点
void CreateListHead(LinkList* l,int n)
{
LinkList p;
int i;
srand(time(0)); //随机数种子
*l = (LinkList)malloc(sizeof(Node)); //产生一个头结点为空的链表l,为变量分配内存
(*l)->next=NULL; //初始化指向空
for (i=0;i<n;i++) //新结点插入l
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
p->next = (*l)->next; //单链表的标准插入方法,p的next指向下一个结点
(*l)->next = p; //l指向p
}
}
e.单链表的创建(尾插法)
void CreateListTail(LinkList* l,int n)
{
LinkList p,r;
int i;
srand(time(0));
*l = (LinkList)malloc(sizeof(Node));
r = *l; //r为指向尾部的结点,尾插法,最重要的是知道尾结点的地址
for (i=0;i<n;i++)
{
p = (LinkList)malloc(sizeof(Node));
p->data = rand()%100+1;
r->next=p; //尾结点的指针指向p
r=p;
}
r->next = NULL; //尾结点指向空
}
f.单链表的清空
Status ClearSList(LinkList *l)
{
LinkList p,q;
p = (*l)->next;
while (p)
{
q=p->next;
free(p);
p=q;
}
(*l)->next=NULL; //头结点指向空
return OK;
}
双链表
循环链表
静态链表
f:测试入口
void main()
{
/*
//线性表顺序存储的测试用例
SqList l;
l.length = 10;
ElemType e;
for (int i=0;i<l.length;i++)
{
l.data[i]=i;
}
GetElem(l,3,&e);
ListInsert(&l,3,5); //在第三个位置插入5
ListDelete(&l,3,&e);
printf("%d\n",l.data[2]); //&e表示引用e的地址,e为一个变量;*e表示e指向的内容,e为一个指针变量
system("pause");
*/
/*
单链表的单元测试
*/
ElemType e;
LinkList m; //声明一个结点
m = NULL; //结点初始化为空
CreateListHead(&m,10); //头插法创建单链表
//printList(&m);
CreateListTail(&m,10); //尾插法创建链表
printList(&m);
LinkListDelete(m,2,&e); //单链表删除第i个元素,返回给e
printList(&m);
LinkListInsert(m,2,99); //单链表的插入,第2个位置插入99
printList(&m);
GetElemLinkList(m,2,&e); //单链表的查询,获取第2个元素的值,返回给e
printf("%d\n",e);
ClearSList(&m); //清空单链表
printList(&m);
system("pause");
}
循环链表的实现
单链表中终点的指针指向起点,就形成单链循环表;
在实际中为了快速的找到头结点、尾结点;常常设置一个头结点(空),将头指针改为尾指针
循环链表插入结点
1.判断:插入位置是否超限;超如位置是否在头/尾(设计头尾指针的移动)
1.在头结点位置插入情况 将尾结点指针指向新结点
将新结点指向头结点
将头结点指针指向新结点
2. 在其他位置插入:
不断next到需要插入的位置上一位置;
将新结点指向下一位置
将上结点指向新结点
如果插入为尾结点,将尾指针移动
实现如下:
def insert_node(self,index:int,data:Any):
"""根据索引插入结点"""
#判断索引是否超出范围,超出范围报错
if index <0 or index>len(self):
raise IndexError("index out of range!")
#如果为插入的链表为空,直接插入不改变
new_node = Node(data) #创建一个新的结点
if self.head ==None: #相当于初始化
new_node.next = new_node #第一个结点指向自己
self.tail=self.head = new_node #头结点和尾结点都在第一个结点
elif index == 0:
#在头插入
new_node.next = self.head #1 新结点的指针指向头结点
self.tail.next = new_node #2 将尾结点指向新结点
self.head = new_node #3 head的位置移动到新结点
else:
temp = self.head #从头开始
for _ in range(index-1):#一个一个查询链表的next知道index位置
temp = temp.next
new_node.next = temp.next #新结点指向插入位置下一结点
temp.next = new_node #上结点指向新结点
#如果index为tail,应该将tail位置更新
if index == len(self)-1:
self.tail = temp.next
循环链表的删除
删除与插入可以看做相同过程
双链表结构
在单链表中,每个结点设置一个前驱一个后驱的指针