1、具有相同的数据类型的有限序列,n为表长,如果n = 0为空表。
2、位序是1开始,数组下标是0开始。
3、基本操作,将对数据结构基本操作进行封装,降低出错风险。
InitList(int &L);//初始化
DestoryList(int &L);//销毁ListInsert(int &L,e);//插入
ListDelete(int &L,e,i);//删除LocalElem(int L,int e);//按值查找
GetElem(int L,int e);//按位查找
C++写法:将参数结果带回来 -> &
C写法:使用指针修改
定义:存储结构为顺序存储的线性表
typedef struct{ElemType data[Max];int length;
}Sqlist; //链表初始化
void InitList(Sqlist *L){L->length = 0;
}
定义和初始化
typedef struct{ElemType *data;int length;int max;
}List;void InitList(List *L){L->data = (int *)malloc(sizeof(ElemType) * Max);L->length = 0;L->max = Max;
}void IncreaseList(List *L,int len){int *p = L->data;int i;L->data = (int *)malloc(sizeof(int) * (len + L->max));for(i = 0;i < L->length;i++){L->data[i] = p[i];}free(p);L->max = L->max + len;
}
静态分配:定义数据结构:数组+长度,使用基本操作去访问顺序表
动态分配:数据结构:数组的指针+长度,可以使用malloc函数去扩展数组的长度。
顺序表特点:
可以实现随机访问,存储密度高
修改比较困难,需要移动大量元素。
插入操作:
1、判断插入位置是否有效
2、判断表是否已满
3、从表底从下向上一个个搬运,表长度+1,并且返回true
4、时间复杂度O(n)
//插入操作
bool ListInsert(Sqlist *L,int i,int e){int j;if(i < 1 || i > L->length + 1){return false;}if(L->length >= Max){return false;}for(j = L->length;j >= i;j--){L->data[j] = L->data[j-1];}L->data[i-1] = e;L->length++;return true;
}
删除操作:
1、判断是否位置是否满足
2、从i节点从后往前转移
3、表长度-1
bool ListDelete(Sqlist *L,int i,int *e){int j;if(i < 1 || i > L->length){return false;}if(L->length == 0){return false;}*e = L->data[i-1];for(j = i;j < L->length;j++){L->data[j-1] = L->data[j];}L->length--;return true;
}
//按位查找
ElemType GetElem(Sqlist L,int i){return L.data[i-1];
}
//按值查找
ElemType LocalEleme(Sqlist L,int i){int j = 0;for(j = 0;j < L.length;j++){if(L.data[j] == i){return j+1;}}
}
按值查找&按位查找
按位查找:时间复杂度O(1)
按值查找:时间复杂度O(n)
1、物理结构是链式存储的线性表
2、前后关系使用指针
3、两种实现:带头结点和不带头结点
#include
#include
#include
typedef struct LNode{int data;struct LNode* next;
}LNode,*LinkList;LNode* GetElem(LinkList L,int i){int j = 1;LNode *p = L;if(i == 0){return L;}if(i < 1){return NULL;}for(j = 1;j < i && p!=NULL;j++){p = p->next;}return p;
}//不带头结点初始化:
bool InitList(LinkList* L){(*L) = NULL;return true;
}//定义带头结点:
bool ListInit(LinkList *L){*L = (LinkList)malloc(sizeof(LNode));if(L == NULL){return false;}(*L)->next = NULL;return true;
} int main(int argc,char const *argv[]){LinkList L;InitList(&L);printf("%d\n",L->data);return 0;
}
1、按位序插入(带头结点)
思路:
//按位序插入(带头结点)
bool ListInsert(LinkList *L,int i,int e){int j = 0;LNode *p = (*L);if(i < 1){return false;}while(p!=NULL && j < i-1){p = p->next;j++;}return InsertNextNode(p,e);
}
插入关键是:
s->data = e;
s->next = p->next;
p->next = s;
如果是不带头结点的单链表,第一位插入的情况,p是指第一个节点,需要特殊处理:
if(i = 1){LNode *s = (LNode*)malloc(sizeof(LNode));s->data = e;s->next = p;p = s;return true;
}
将后插操作进行分装成InsertNextNode
//在一个节点后插操作
bool InsertNextNode(LNode *p,int 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;
}
前插操作:
///在节点前插操作
bool InsertPirorNode(LNode *p,int e){if(p == NULL){return false;}LNode *s = (LNode*)malloc(sizeof(LNode));if(s == NULL){return false;}s->next = p->next;p->next = s;s->data = p->data;p->data = e;return true;
}
思路:
//带头结点
bool ListDelete(LinkList *L,int i,int *e){LNode *p = *L;int j = 0;if(i < 1){return false;} while(p!=NULL && j < i-1){p = p->next;j++;}if(p==NULL || p->next == NULL){return false;}LNode *q = p->next;*e = q->data;p->next = q->next;free(q);
}
//不带头结点
bool DeleteList(LinkList *L,int i,int *e){LNode *p = *L;int j = 0;if(i < 1){return false;} if(i == 1){if(L==NULL){return false;}LNode *q = p;p = p->next;*e = q->data;free(q);}while(p!=NULL && j < i-1){p = p->next;j++;}if(p==NULL || p->next == NULL){return false;}LNode *q = p->next;*e = q->data;p->next = q->next;free(q);
}
指定节点的删除
bool DeleNode(LNode *p){if(p->next == NULL){free(p);return true;}LNode *q = p->next;p->data = q->data;p->next = q->next;free(q);return true;
}
使用前插操作,但如果是最后一个节点,只能从头开始遍历,找到i-1个节点,然后修改next指针。
LNode* GetElem(LinkList L,int i){int j = 0;LNode *p = L;if(i < 1){return NULL;}for(j = 1;j < i && p!=NULL;j++){p = p->next;}return p;
}
时间复杂度O(n)
LNode * LocalElem(LinkList L,int 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->next;while(p!=NULL){p = p->next;len++;}return len;
}
LinkList Link_TailInsert(LinkList *L){int x;*L = (LNode*)malloc(sizeof(LNode));(*L)->next = NULL;scanf("%d",&x);LNode *s,*r = *L;while(x!=99999){s = (LNode*)malloc(sizeof(LNode));s->data = x;r->next = s;r = s;scanf("%d",&x);}r->next = NULL;return *L;
}
思路:
利用2个指针,s用于新建,r用于标记最后一个指针。
LinkList Link_HeadInsert(LinkList *L){*L = (LNode*)malloc(sizeof(LNode));int x;scanf("%d",&x);while(x!=999){InsertNextNode(&L,x);scanf("%d",&x);}
}
利用后插操作,对头结点一直进行后插操作,可以实现链表的逆置。
定义:
typedef struct DNode{int data;struct DNode *prior,*next;
}DNode,*DList;
初始化:
bool InitDLinkList(DLink *L){*L = (DNode*)malloc(sizeof(DNode));if(*L == NULL){return false;}(*L)->piror = NULL;(*L)->next = NULL;return true;
}
后插操作:
需要考虑p后续节点是否为空
bool InsertNextDNode(DNode *p,DNode *s){s->next = p->next;if(p->next!=NULL){p->next->prior = s;}s->prior = p;p->next = s;return true;
}
删除操作:
注意要删除的节点后续有没有节点,如果有节点需要修改前指针的操作
bool DeleteNextDNode(DNode *p){if(p == NULL){return false;}DNode *q = p->next;//如果没有后继 if(q == NULL){return false;}if(q->next!=NULL){q->next->prior = p;} p->next = q->next;free(q);
}
void DestoryDLisk(DLisk *p){while(p->next! = NULL){DeleteNextDNode(p);}free(p);//释放头结点 p = NULL;
}
销毁双指针的方法就是不断调用头指针,销毁下一个指针。
循环链表表示,尾结点的next指针指向头结点。如果经常进行表头表尾操作,则可以使用循环链表。
循环链表判空操作:
本质是物理上是数组的连续存储,逻辑上是链表的随机存储。用数组的方式实现的链表。数组元素固定不变的场合:FAT
1、逻辑结构:
按照线性的结构,第i个表示位序。除了第一个元素外,都有前驱。除了最后一个,都有后继
2、存储结构:
顺序表:数组, 要求连续存储,存储密度高,支持随机存取。
链表:离散存储,不可随机存取,存储密度低
3、操作:
顺序表 | 链表 | |
---|---|---|
增 | 不方便,需要连续大片区域 | 只需要声明指针,和节点 |
删 | 静态数组自动回收,动态分配需要使用free | 使用free,时间开销低 |
查 | 可以随机存取,时间开销低 | 顺序查找,时间开销高 |
上一篇: 友情没有愚人节