线性表基本操作总结_顺序表_链表

文章目录

  • 线性表
    • 存储结构
      • 1.顺序表(顺序存储)
        • ① 静态分配
        • ② 动态分配
        • 基本操作
          • ① 插入
          • ② 删除
          • ③ 按位查找
          • ④ 按值查找
      • 2.链表(链式存储)
        • ① 单链表
          • 初始化
          • 插入
          • 删除
          • 查找
          • 建立
        • ② 双链表
          • 初始化
          • 插入
          • 删除
          • 遍历
        • ③ 循环单链表
          • 初始化
        • ④ 循环双链表
          • 初始化
        • ⑤ 静态链表
          • 初始化

线性表

存储结构

1.顺序表(顺序存储)

① 静态分配

定义

#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;
}
int main()
{
    SqList L;    //声明
    InitList(L);    //初始化
}
② 动态分配
#define InitSize 10    //初始长度
typedef struct{
    ElemType *data;    //指示动态分配数组的指针
    int MaxSize;        //最大容量
    int length;        //当前长度
}SeqList;

初始化

void InitList(SeqList &L){
    L.data = (int *)malloc(InitSize * sizeof(int));    //设int型
    L.length = 0;
    L.MaxSize = InitSize;
}

增加动态数组的长度

//malloc 动态扩展顺序表的最大容量,最后需要free函数释放原区域
void IncreaseSize(SeqList &L, int len){
    int *p = L.data;    //用p指向原来的数组的起始地址
    L.data = (int *)malloc((L.MaxSize + len)*sizeof(int));
    for(int i = 0; i < L.length; i++)    L.data[i] = p[i];    //数据复制到新区域,时间开销大
    L.MaxSize = L.Maxsize + len;
    free(p);    //释放原来的内存空间
}
基本操作
① 插入
//将元素e插入到L的第i个位置    1 <= i <= length+1
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返回       1 <= i <= length
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; j < L.length; j++) L.data[j-1] = L.data[j];    //从第i+1位置开始,向前移动
    L.length --;
    return true;
}

tips: 添加时移动元素是从最后一个元素开始后移,删除时是从第i+1个位置开始前移

③ 按位查找
int GetElem(SqList L, int i){
    return L.data[i-1]
}
④ 按值查找
int LocateElem(SqList L,int e){
    for(int i = 0; i < L.length; i++)
        if(L.data[i] == e) return i+1;
    return 0;
}

2.链表(链式存储)

① 单链表

定义

typedef struct LNode{
    ElemType data;
    struct Node *next;
}LNode, *LinkList;

tips: 使用LNode*用于强调是结点,使用LinkList用于强调是单链表

初始化
//不带头结点
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;
}
插入
  • 按位序插入
//有头结点,对于在第一个位置插入不需特殊处理,因为0号结点就是头结点,相当于在头结点后插
bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1) return false;
    LNode *p;    //指向当前扫描到的结点
    int j =0;    //当前p指向的是第几个结点
    p = L;        //p指向头结点
    while(p != NULL && j < i-1){    //找到第i-1个结点,即插入位置的前一个结点
        p = p->next;
        j++;
    }
    if(p == NULL) return false;
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s -> data = e;
    s -> next = p -> next;
    p -> next = s;
    return true;
}
//无头结点,当在第一个位置插入需要特判
bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1) return false;
    if(i == 1)
    {
        LNode *s = (LNode *)malloc(sizeof(LNode));
        s -> data = e;
        s -> next = L;
        L = s;        //头指针指向第一个结点
        return true;
    }
    LNode *p;    //指向当前扫描到的结点
    int j = 1;    //当前p指向的是第几个结点
    p = L;        //p指向第一个结点
    while(p != NULL && j < i-1){    //找到第i-1个结点,即插入位置的前一个结点
        p = p->next;
        j++;
    }
    if(p == NULL) return false;
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s -> data = e;
    s -> next = p -> next;
    p -> next = s;
    return true;
}
  • 指定结点的后插
bool InsertNextNode(LNode *p, ElemType e){
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s -> data = e;
    s -> next = p -> next;
    p -> next = s;
    return true;
}
  • 指定结点的前插
//法1:引入头指针,从头指针遍历找到p的前驱结点q,再对q后插
//法2:偷天换日,实际是尾插,但将其data替换
bool InsertPriorNode(LNode *p, ElemType e){
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s -> next = p -> next;
    p -> next = s;
    s -> data = p -> data;
    p -> data = e;
    return true;
}
删除

需要找到前驱节点

  • 按位序删除(带头结点)
bool ListDelete(LinkList &L,int i,ElemType &e){
    if(i<1) return false;
    LNode *p;    //指向当前扫描到的结点
    int j =0;    //当前p指向的是第几个结点
    p = L;        //p指向头结点
    while(p != NULL && j < i-1){    //找到第i-1个结点,
        p = p->next;
        j++;
    }
    if(p == NULL) return false;
    if(p->next == NULL)  return false;
    LNode *q = p -> next;
    e = q -> data;
    p -> next = q -> next; 
    free(q);
    return true;
}
  • 指定结点的删除
//法1:引入头指针,从头指针遍历找到p的前驱结点q
//法2:偷天换日
bool DeleteNod(LNode *p){
    if(p == NULL) return false;
    LNode *q = p->next;
    p -> data = q -> data;    //如果p是最后一个节点,那么q不存在,只能从表头开始寻找p的前驱
    p -> next = q -> next;
    free(q);
    return true;
}
查找
  • 按位查找
LNode * GetElem(LinkList L,int i){
    if(i<0) return false;
    LNode *p;    //指向当前扫描到的结点
    int j =0;    //当前p指向的是第几个结点
    p = L;        //p指向头结点
    while(p != NULL && j < i){    //找到第i个结点
        p = p->next;
        j++;
    }
    return p;
}
  • 按值查找
LNode * LocateElem(LinkList L,ElemType e){
    LNode *p = L->next;
    while(p != NULL && p->data != e)    
        p = p->next;
    return p;
}
建立
  • 尾插法

相当于对尾结点的后插,则可以设立一个表尾指针,每次插入一个结点后将其修改。

LinkList List_TailInsert(LinkList &L){
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //建立头结点
    LNode *s,*r = L;    //r为表尾指针
    cin >> x;
    while(x!=9999)    //设立一个特殊值表示结束
    {
        s = (LNode *)malloc(sizeof(LNode));
        s -> data = x;
        r -> next = s;
        r = s;    //r指向新的表尾
        cin >> x;
    }
    r -> next = NULL;
    return L;
}
  • 头插法(链表的逆置)

相当于对头结点的后插

LinkList List_HeadInsert(LinkList &L){
    int x;
    L = (LinkList)malloc(sizeof(LNode));    //建立头结点
    L -> next = NULL;
    LNode *s;
    cin >> x;
    while(x!=9999)    //设立一个特殊值表示结束
    {
        s = (LNode *)malloc(sizeof(LNode));
        s -> data = x;
        s -> next = L -> next;
        L -> next = s;
        cin >> x;
    }
    return L;
}
//给定一个L,如何逆置
LinkList List_HeadInsert(LinkList L,LinkList &L2){
    int x;
    L2 = (LinkList)malloc(sizeof(LNode));    //建立头结点
    L2 -> next = NULL;
    LNode *s;
    LNode *p = L->next;
    while(p != NULL){
        x = p -> data;
        s = (LNode *)malloc(sizeof(LNode));
        s -> data = x;
        s -> next = L2 -> next;
        L2 -> next = s;
    }
    return L2;
}
```
② 双链表

定义

typedef struct LNode{
    ElemType data;
    struct Node *prior,*next;
}LNode, *DLinkList;
初始化
L->prior = NULL;
L->next = NULL;
插入
//p结点后插入s结点
s->next = p->next;
if(p->next != NULL) p->next->prior = s;    //如果p有后继
p->next = s;
s->prior = p;
删除
//删除p结点的后继结点
DNode *q = p->next;
p->next = q->next;
if(q->next != NULL) q->next->prior = p;
free(q);
遍历
//后向
while(p!=NULL) p=p->next;
//前向
while(p!=NULL) p=p->prior;
//前向(跳过头结点)
while(p->prior!=NULL) p=p->prior;
③ 循环单链表
初始化
L->next = L;    //判空

判断p是否为表尾结点

p->next == L;
④ 循环双链表
初始化
L->prior = L;
L->next = L;    //判空

判断p是否为表尾结点

p->next == L;

插入删除时,不用判断

//p后插入s
s->next = p->next;
p->next->prior = s;   
p->next = s;
s->prior = p;

//删除p结点的后继结点
DNode *q = p->next;
p->next = q->next;
q->next->prior = p;
free(q);
⑤ 静态链表

定义

typedef struct{
    ElemType data;
    int next;    //下一个元素的数组下标
}SLinkList[MaxSize];
SLinkList a;
初始化
a[0] = -1;

你可能感兴趣的:(链表,数据结构,算法)