定义
线性表是具有相同数据类型的 n ( n ≥ 0 ) n(n≥0) n(n≥0) 个数据元素的有限序列,其中 n n n 为表长,当 n = 0 n = 0 n=0 时线性表是一个空表。若用 L L L 命名线性表,则其一般表示为
L = ( a 1 , a 2 , … , a i , a i + 1 , … , a n ) L = (a_1,a_2,…,a_i,a_{i+1},…,a_n) L=(a1,a2,…,ai,ai+1,…,an)
顺序表:用顺序存储的方式实现线性表
特点:
静态分配
#define MaxSize 10 //定义最大长度
typedef struct{
ElemType data[MaxSize]; //用静态的“数组”存放数据元素
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义(静态分配方式)
// 初始化
void InitList(SqList &L){
for(int i=0; i<MaxSize; i++)
L.data[i]=0;
L.length=0;
}
动态分配
#define InitSize 10
typedef struct{
int *data;
int MaxSize;
int length;
}SeqList;
// 初始化
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;
L.data= (int *)malloc((L.Maxsize+len)*sizeof(int));
for(int i=0;i<L.length;i++)
L.data[i]=p[i];
L.MaxSize+=len;
free(p);
}
#include
#define MaxSize 10 //定义最大长度
typedef struct{
int data[MaxSize]; //用静态的“数组”存放数据元素
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义(静态分配方式)
// 初始化
void ListInit(SqList &L){
for(int i=0; i<MaxSize; i++)
L.data[i] = 0;
L.length = 0;
}
// 创建顺序表
void ListSet(SqList &L,int *arr,int len){
L.length = len;
for(int i=0;i<len;i++){
L.data[i] = arr[i];
}
}
// 输出
void ListDisplay(SqList &L){
for(int i=0;i<L.length;i++)
printf("%d ",L.data[i]);
printf("\n");
}
// 插入:在L的位序i处插入元素e
// 时间复杂度:O(n)
bool ListInsert(SqList &L, int i, int e){
if(i < 1||i > L.length+1) //处理异常情况
return false;
if(L.length >= MaxSize) //【易漏】处理异常情况
return false;
for(int j=L.length;j>=i;j--){
L.data[j] = L.data[j-1];
}
L.data[i-1] = e;
L.length++;
return true;
}
// 删除:删除L中位序为i的元素e
// 时间复杂度:O(n)
bool ListDelete(SqList &L, int i,int &e){
if(i < 1||i > L.length) //处理异常情况
return false;
e = L.data[i-1];
for(int j=i-1; j<L.length-1; j++){
L.data[j]=L.data[j+1];
}
L.length--;
return true;
}
// 按位查找
// 时间复杂度:O(1)
bool GetElem(SqList L,int i,int &e){
if(i < 1||i > L.length) //处理异常情况
return false;
e = L.data[i-1];
return true;
}
// 按值查找:查找第一个元素值等于e的元素,位序赋值给i
// 时间复杂度:O(n)
bool SelectElem(SqList L,int &i, int e){
for(int j=0;i<L.length;i++){
if(L.data[i] == e){
i = j;
return true;
}
}
return false;
}
顺序表与单链表的对比
顺序表
单链表
// 定义单链表结类型
typedef struct LNode{
ElemType data; // 数据域
struct LNode *next; // 指针域
}LNode, *LinkList;
//等价于
typedef struct LNode LNode;
typedef struct LNode *LinkList;
LNode *L; // 声明一个指向单链表第一个结点的指针
LinkList L; // 声明一个指向单链表第一个结点的指针
//增加一个新的结点:在内存中申请一个结点所需空间,并用指针 p 指向这个结点
struct LNode * p = (struct LNode *) malloc(sizeof(struct LNode));
bool InitList(LinkList &L){
L = NULL;
return true;
}
bool InitList(LinkList &L){
L = (LNode *)malloc(sizeof(LNode));
if(L == NULL)
return false;
L->next = NULL;
return true;
}
LNode * GetElem(LinkList L,int i){
if(i < 0)
return NULL;
LNode *p = L;
int j = 0;
while(p != NULL && j < i){
p = p->next;
j++;
}
return p;
}
LNode * SelectElem(LinkList L,ElemType e){
LNode *p = L->Next;
while(p != NULL && p->data != e)
p = p->next;
return p;
}
int length(LinkList L){
int len = 0;
LNode *p = L;
while(p->next != NULL){
p = p->next;
len++;
}
return len;
}
// 在第 i 个位置上插入元素e
// 时间复杂度:O(n)
bool ListInsert(LinkList &L,int i,ElemType e){
if(i < 1) // 处理不合法数据
return false;
LNode *p; // 指针p指向当前扫描到的结点
int j = 0; // 当前指针指向的是第几个结点
p = L; // L指向头结点
// p = GetElem(L,i-1);
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; //【注意】最后两句顺序不可颠倒
p->next = s;
return true;
}
// 在第 i 个位置上插入元素e
// 时间复杂度:O(n)
bool ListInsert(LinkList &L,int i,ElemType e){
if(i < 1) // 处理不合法数据
return false;
if(i == 1){ // 【注意】需要额外处理插入第1个结点的情况
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s; // 头指针指向新结点
return true;
}
LNode *p; // 指针p指向当前扫描到的结点
int j = 1; // 当前指针指向的是第几个结点
p = L; // L指向头结点
// p = GetElem(L,i-1);
while(p != NULL && j < i-1){ // 找到第i-1个结点
p = p->next;
j++;
}
// InserNextNode(p,e);
if(p == NULL) // i值不合法
return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
s->next = p->next; //【注意】最后两句顺序不可颠倒
p->next = s;
return true;
}
// 后插操作:在p结点之后插入元素e
// 时间复杂度:O(1)
bool InsertNextNode(LNode *p,Elemtype 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;
}
// 前插操作:在p结点之前插入元素e
bool InserPriorNode(LNode *p,Elemtype e){
if(p == NULL)
return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
if(s == NULL)
reutrn false;
s->next = p->next; // 新结点s连接到p结点后
p->next = s;
s->data = p->data; // p->data数据赋值给s
p->data = e; // e赋值给p->data
}
// 删除第i个位置的元素,e返回删除的元素值
// 时间复杂度:O(n)
bool ListDelete(LinkList &L,int i,ElemType &e){
if(i < 1) // 处理不合法数据
return false;
LNode *p = L;
int i=0;
// GetElem(L,i-1);
while(p != NULL && j < i-1){ // 找到第i-1个结点
p = p->next;
j++;
}
if(p == NULL) // 值不合法
return fasle;
if(p->next == NULL) // 第i-1个结点后已无其他结点
return fasle;
e = p->next->data;
p->next = p->next->next;
return true;
}
指定结点删除:需要修改其前驱结点的next指针
// 删除指定结点p
// 时间复杂度:O(1)
bool DeleteNode(LNode *p){
if(p == NULL)
return false;
LNode *q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
return true;
}
LinkList List_TailInsert(LinkList &L){
int x;
L = (LinkList)malloc(sizeof(LNode));
LNode *s,*p = L; // p指向当前已插入的最后一个结点
scanf("%d",&x);
while(x!=9999){
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
p->next = s;
p = s; // p指向最后一个结点
scanf("%d",&x);
}
p->next = NULL;
return L;
}
LinkList List_HeadInsert(LinkList &L){
int x;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL; // 【好习惯】只要是初始化单链表,就将头指针指向 NULL
LNode *s;
scanf("%d",&x);
while(x!=9999){
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
void NoNode_HeadInsert(LinkList *L) {
LNode *s;//要插入的节点
int x;//要插入的元素
scanf("%d", &x);
while (x != 9999) {
s = (LNode *)malloc(1, sizeof(LNode));
s->data = x;
s->next = NULL;
if (NULL == *L) {
*L = s; //将新结点置位首节点也是尾结点
} else {
s->next = *L;
*L = s; //新结点置位尾结点
}
scanf("%d", &x);
}
}
LinkList Reverse(LinkList &L){
LNode *p,*q;
p = L->next; // p指向当前准备插入的结点
L->next = NULL; // 重新构造单链表
while(p != NULL){
q = p;
p = p->next;
q->next = L->next; // 头插法
L->next = q;
}
return L;
}
typedef struct DNode{
ElemType data;
struct DNode *prior, *next;
}DNode, *DLinkList;
// 初始化双链表
bool InitDLinkList(DlinkList){
L = (DNode *)malloc(sizeof(DNode));
if(L == NULL)
return false;
L->prior = NULL;
L->next = NULL;
return true;
}
// 在p结点之后插入s结点
bool InsertNextDNode(DNode *p,DNode *s){
if(p == NULL || s == NULL)
return false;
s->next = p->next;
if(p->next != NULL)
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
// 删除p结点的后继结点
// 时间复杂度:O(1)
bool DeleteNextDNode(DNode *p){
if(p == NULL )
return false;
DNode *q = p->next; // q是p的后继结点
if(q == NULL) // p的后继结点存在
return false;
p->next = q->next; // 改变p的后继结点
if(q->next != NULL) // 如果q的后继结点存在
q->next->prior = p; // 改变q的后继结点的前驱结点
free(q);
return true;
}
// 销毁表
// 时间复杂度:O(1)
void DestoryList(DLinkList &L){
while(L->next != NULL)
DeleteNextDNode(L);
free(L); // 释放头结点
L = NULL; // 头指针指向NULL
}
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
// 初始化
bool InitList(LinkList &L){
L = (LNode *)malloc(sizeof(LNode));
if(L == NULL)
return false;
L->next = L; // 头结点next指向头结点
return true;
}
// 判空
bool Empty(LinkList L){
if(L->next == L) return true;
else return false;
}
// 判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L,LNode *p){
if(p->next == L) return true;
else return false;
}
typedef struct DNode{
ElemType data;
struct DNode *prior, *next;
}DNode, *DLinkList;
// 初始化
bool InitDLinkList(DLinkList &L){
L = (DNode *)malloc(sizeof(DNode));
if(L == NULL)
return false;
L->prior = L; // 头结点 prior 指向头结点
L->next = L; // 头结点的 next 指向头结点
return true;
}
// 判空
bool Empty(DLinkList L){
if(L->next == L) return true;
else return false;
}
// 在p结点之后插入s结点
s->next = p->next;
p->next->prior = s;
p->next = s;
s->prior = p;
// 删除p的后继结点q
p->next = q->next;
q->next->prior = p;
free(q);
问题:请描述顺序表和链表的bla bla bla…实现线性表时,用顺序表还是链表好?
顺序表 | 链表 | |
---|---|---|
弹性(可扩容) | ||
增、删 | ||
查 |
表长难以预估、经常要增加/删除元素——链表
表长可预估,查询(搜索)操作较多——顺序表