线性结构——顺序表与链表,循环链表和双向链表

顺序表

顺序存储是指在内存中用地址连续的一块存储空间顺序存放线性表的各元素

-元素顺序和地址顺序相同(下标能直接反应元素间的关系)元素的物理位置相邻

-由一维数组实现(但一维数组不一定是顺序存储结构)


顺序存储结构类型定义

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define MAXSIZE 100
#define ERROR -1

typedef int ElementType;
typedef int Position;
typedef struct LNode *PtrToLNode;
struct LNode{
    ElementType Data[MAXSIZE];
    Position Last;
};
typedef PtrToLNode List; //指向结构体的指针

基本操作原型声明 

List MakeEmpty(void); //初始化,生成一个空表
ElementType FindKth(List L, int K);
Position Find(List L, ElementType X);
bool Insert(List L, ElementType X, int i);
bool Delete(List L, int i);
int Length(List L);
bool Empty(List L); //处理中需要可使用
void Print(List L); //输出

初始化,生成一个空表 

List MakeEmpty(void) //初始化,生成一个空表
{
    List L;
    L = (List)malloc(sizeof(struct LNode));
    L->Last = -1;
    return L;
}

求顺序表中第k个元素的值 O(1)

ElementType FindKth(List L, int K) //求顺序表中第k个元素的值
 {
    if (K<=0 || K>L->Last+1 ) {
        printf("查找位序%d不正确\n",K);
        return false;
    }
    else return(L->Data[K-1]);
}

根据元素值X,查找并返回元素位置 O(n)

Position Find(List L, ElementType X) //在线性表L中,找到元素值为X的元素,返回元素位置
{
    Position i = 0;
    while( i <= L->Last && L->Data[i]!= X )
        i++;
    if (i > L->Last) return ERROR; // 如果没找到,返回错误信息
    else return i+1; // 找到后返回的是位序
}

在第i位插入新元素O(n)

-从最后一个元素开始,向后移动元素(改写位序)

bool Insert(List L, ElementType X, int i)
{
    Position j;
    if ( L->Last == MAXSIZE-1) {
        printf("表已满,不能插入\n");
        return false;
    }
    if (i<1 || i>L->Last+2) {
        printf("插入位序%d 不合法\n",i);
        return false;
    }
    for(j=L->Last; j>=i-1; j--) /* Last指向序列最后元素 */
        L->Data[j+1] = L->Data[j]; /* 将位序i及以后的元素顺序向后移动 */
    L->Data[i-1] = X; /* 新元素插入第i位序,其数组下标为i-1*/
    L->Last++; /* Last仍指向最后元素 */
    return true;
}

删除位序为i的元素O(n)

bool Delete(List L, int i) //从L中删除指定位序i的元素,该元素数组下标为i-1
{
    Position j;
    if(i<1 || i>L->Last+1) { /*检查空表及删除位序的合法性*/
        printf("要删除的位序%d不存在元素\n", i);
        return false;
    }
    for(j = i; j <= L->Last; j++)
        L->Data[j-1] = L->Data[j]; /*将位序i+1及以后的元素顺序向前移动*/
    L->Last--; /* Last仍指向最后元素 */
    return true;
}
    //也可以这样写
    for(j = i-1; j < L->Last; j++)
        L->Data[j] = L->Data[j+1]; /*将下标i-1及以后的元素顺序向前移动*/
    L->Last--; /* Last仍指向最后元素 */

求顺序表的长度,返回表中元素个数,空表返回0 

int Length(List L) //求顺序表的长度,返回表中元素个数,空表返回0
{
    if (L->Last==-1)
        return 0;
    else return(L->Last+1);
}

判断线性表L是否为空

bool Empty(List L)
{
    if (L->Last==-1)
        return 0; //表空返回0
    else return 1; //不空返回1
//根据实际情况也可设定空返回1,不空返回0
}

按顺序输出顺序表所有元素 

void Print(List L)
{
    for (int i=0; i<=L->Last; i++)
        printf("%d ", L->Data[i]);
    printf("\n\n");
}

 对各操作的简单测试

int main(void)
{
    List LPtr;
    LPtr=MakeEmpty();
    if (!Empty(LPtr)) printf("现在表是空的\n");
    Insert(LPtr,5,1);
    Insert(LPtr,3,2);
    Insert(LPtr,1,3);
    Insert(LPtr,7,4);
    Insert(LPtr,8,5);
    Insert(LPtr,2,6);
    Insert(LPtr,9,7);
    printf("已插入7个元素:");
    Print(LPtr);
    printf("表的长度为:%d\n\n",Length(LPtr));
    printf("第五个数为:%d\n\n",FindKth(LPtr,5));
    
    printf("查找值为7的数在第%d位\n\n",Find(LPtr,7));
    
    Delete(LPtr,4);
    printf("删除第4个元素后,表的数据为:");
    Print(LPtr);
    printf("删除后表的长度为:%d\n\n",Length(LPtr));
    Delete(LPtr,8);
    
    printf("在第-1个位置之前插入元素10:");
    Insert(LPtr,10,-1);
    printf("在第10个位置之前插入元素10:");
    Insert(LPtr,10,10);
    return 0;
}


链表

通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起

-结点:数据域(结点信息Data)+指针域/链域(相邻结点的地址)

-结点存储自身信息和直接后继信息

-单链表:每个结点只有一个链域

-头指针指向开始结点/头结点(设置头结点可以统一空表和非空表的操作)


单链表类型定义 

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define ERROR -1

typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode
{
    ElementType Data;
    PtrToLNode Next; //等同于struct LNode *Next;
};
typedef PtrToLNode Position; //这里的位置是结点的地址
typedef PtrToLNode List;

基本操作原型声明

List MakeEmpty(void); //初始化,生成一个带表头结点的空链表
ElementType FindKth(List L, int K); //在线性表L中,找到第K个元素
Position Find(List L, ElementType X); //在线性表L中,找到元素值为X的元素,返回元素位置
bool Insert(List L, ElementType X, int i); //在线性表L中,第i个元素之前插入值为X的元素
bool Delete(List L, int i); //在线性表L中,删除第i个元素
int Length(List L); //求线性表L的长度,即元素个数
bool Empty(List L); //判断线性表L是否为空
void Print(List L); //输出线性表所有元素

基本操作函数定义 

创建空链表

List MakeEmpty(void)
{
    List Lptr;
    Lptr=(PtrToLNode)malloc(sizeof(PtrToLNode)); //申请一个新的结点并指向该结点
    if(Lptr==NULL) exit(1); //如果申请失败,即存储空间分配失败,退出程序
    Lptr->Next=NULL; //头结点指针域置空
    return Lptr;
}

 返回链表的长度O(n)

-最后一个是p->Next = NULL

int Length(List L) 
{
    Position p;
    int cnt = 0; /* 初始化计数器 */
    p = L; /* p指向表的头结点 */
    while (p) {
        p = p->Next;
        cnt++; /* 当前p指向的是第cnt个结点*/
    }
    return cnt;
}

根据指定的位序K,返回查找的相应元素O(n)

-非随机存取,不能直接按序号访问,只能从头指针出发顺Next域逐个结点往下搜索并记录当前元素位序

-跟查找单链表的长度算法只有return的结果不一样

ElementType FindKth(List L, int K) //根据指定的位序K,返回L中相应元素
{
    Position p; //等同于PtrToLNode p或struct Lnode *p
    int cnt = 0; /* 位序从0开始 */
    p = L; /* p指向头结点 */
    while (p && cnt<K) {
        p = p->Next;
        cnt++;
    }
    if ((cnt==K) && p)
        return p->Data; //找到第K个
    else
        return ERROR; //否则返回错误信息,此处错误信息为-1,与ElementType类型一致
}

根据指定的元素X,返回所查找的结点的地址O(n)

Position Find(List L, ElementType X) 
{
    Position p = L->Next; //p指向L的第1个结点
    while (p && p->Data!=X)
        p = p->Next;
    if (p)
        return p;
    else return NULL;
}

在位序为i的位置插入新节点(默认L有头结点)

​​​​​​​链表中空间的动态分配与回收

    -结点申请 malloc

如:tmp=(Position)malloc(sizeof(struct LNode))

意思是分配一个大小为LNode字节的空间并将首地址放入指针变量tmp(头结点)中

    -结点释放 free()

-先添加在位序为i的元素前面,再添加在位序为(i-1)的元素后面

bool Insert(List L, ElementType X, int i)
{ 
    Position tmp, pre;
    int cnt = 0;
    /* 查找位序为i-1的结点 */
    pre = L; /* pre指向表头结点 */
    while ( pre && cnt<i-1 ) {
        pre = pre->Next;
        cnt++;
    }
    if ( pre==NULL || cnt!=i-1) { /* 所找结点不在L中 */
        printf("插入位置参数错误\n\n");
        return false;
    }
    else { /* 找到了待插结点的前一个结点pre;若i为1,pre就指向表头 */
        /* 插入新结点 */
        tmp=(Position)malloc(sizeof(struct LNode)); /*申请、填装结点*/
        tmp->Data = X;
        tmp->Next = pre->Next; //先添加至位序为i的元素前面
        pre->Next = tmp; //再让位序为(i+1)的元素的Next域指向tmp
        return true;
    }
}

删除位序为i的节点 

-直接将位序为(i-1)的元素的Next域指向原先位序为(i+1)的元素

bool Delete(List L, int i)
{ /* 这里默认L有头结点 */
    Position tmp, pre;
    int cnt = 0;
    /* 查找位序为i-1的结点 */
    pre = L; /* pre指向表头 */
    while (pre && cnt<i-1) { //找到位序为i-1的结点pre
        pre = pre->Next;
        cnt++;
    }
    if (pre == NULL || cnt != i-1 || pre->Next == NULL) { //所找结点或位序为i的结点不在L中
        printf("删除位置参数错误\n\n");
        return false;
    }
    else { /* 找到了待删结点的前一个结点pre,将结点删除 */
        tmp = pre->Next;
        pre->Next = tmp->Next;
        free(tmp);
        return true;
    }
}

 判断线性表L是否为空

bool Empty(List L) //判断线性表L是否为空
{
    Position p = L->Next;
    if(p != NULL) printf("此线性表L非空\n");
    else printf("此线性表L为空\n");
    return 0;
}

输出线性表所有元素 

void Print(List L) //输出线性表所有元素
{
    Position p = L->Next;
    while(p) {
        printf("%d ",p->Data);
        p = p->Next;
    }
    printf("\n");
}

对各操作的简单测试 

int main(void)
{
    List LPtr;
    Position tmp;
    LPtr=MakeEmpty();
    if (!Empty(LPtr) )
        printf("初始化,现在表是空的\n");
    Insert(LPtr,5,1);
    Insert(LPtr,3,2);
    Insert(LPtr,1,3);
    Insert(LPtr,7,4);
    Insert(LPtr,8,5);
    Insert(LPtr,2,6);
    Insert(LPtr,9,7);
    printf("已插入7个元素:");
    Print(LPtr);
    printf("表的长度为:%d\n\n",Length(LPtr));
    
    printf("第五个数为:%d\n\n",FindKth(LPtr,5));
    
    tmp=Find(LPtr,7);
    if(tmp) printf("查找值为7的数查找成功\n\n");
    else printf("没有值为7的数\n\n");
    
    tmp=Find(LPtr,11);
    if(tmp!=NULL) printf("查找值为11的数查找成功\n\n");
    else printf("没有值为11的数\n\n");
    
    printf("删除第4个元素后,表的数据为:");
    Delete(LPtr,4);
    Print(LPtr);
    printf("删除后表的长度为:%d\n\n",Length(LPtr));
    
    printf("删除第8个元素:");
    Delete(LPtr,8);
    
    printf("在第-1个位置之前插入元素10:");
    Insert(LPtr,10,-1);
    
    printf("在第10个位置之前插入元素10:");
    Insert(LPtr,10,10);
    Empty(LPtr);
    return 0;
}

-注意位序与下标

-单链表的插入和删除运算不需要移动结点,但指针占用额外存储空间


循环链表

-L为头指针,p为尾指针

-最后一个结点的指针域指向头结点

判定为空的条件

L->Next == L;

插入操作

1.普通位置(和单链表相同)

2.头插法

tmp->Next = L->Next;
L->Next = tmp;
p->Next = tmp;

3.尾插法

tmp->Next = L->Next;
p->Next = tmp;

4.第一次插入(L->Next = NULL)

tmp->Next = L->Next;
L->Next = tmp;
tmp->Next = tmp;

删除

1.普通位置/尾结点(和单链表相同)

2.头结点

tmp = L->Next;
L->Next = tmp->Next;
p->Next = tmp->Next;
free(tmp);

双向链表

-L为头指针

结点定义

typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Prior, Next;
}
typedef PtrToLNode Position; //这里的位置是结点的地址
typedef PtrToLNode List;

判定为空的条件

L->Next == L && L->Prior == L

插入

- s为待插入结点

- p为待插入的位置上的结点

- 注意每一步的顺序

s->Prior = p->Prior;
p->Prior->Next = s;
s->Next = p;
p->Prior = s;

删除 

- s为待删除结点

- p为待删除的位置上的结点

s->Prior->Next = s->Next;
p->Prior = s->Prior;
free(s);

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kentos(acoustic ver.)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值