数据结构——线性表

线性表的顺序存储结构——顺序表

在C语言中用一维数组来描述顺序存储结构,如下所示。

//线性表的顺序存储结构
#define MaxSize      //线性表存储空间的初始分配量
typedef struct {
    ELemType data[MaxSize];    //存放顺序表中的元素
    int length;                //存放顺序表的当前长度
} SqList;                      //声明顺序表的类型

  • 顺序表的初始化
    算法 2.1
void InitList(SqList &L)   //由于L要回传给实参,所以用引用类型
{
    L.length = 0;
}

  • 取顺序表中第i个元素
    算法 2.2
int GetElem(SqList L, int i, ElemType &e)    //返回L中第i个元素的值
{
    if(i < 1 || i > L.length)
        return 0;               //i值不合法
    else
        e = L.data[i-1];        //取出第i个元素,位置data[i-1]中
        return 1;
}

时间复杂度为O(1)。


  • 顺序表的查找
    查找顺序表L中与给定的元素x相等的元素,若找到,返回该元素在顺序表中的序号,否则返回0,表示查找失败。
    算法 2.3
int Locate(SqList L,ElemType x) {
    int i = 0;
    while(i < L.length && L.data[i] != x)
        i++;
    if(i >= L.length)
        return 0;             //未找到返回0
    else
        return i + 1;         //找到后返回其逻辑序号
}

时间复杂度为O(n)。


  • 顺序表的插入
    算法 2.4
int ListInsert(SqList &L,int i,ElemType x) {    //在顺序表L中第i个位置之前插入新的元素x
    int j;
    if(i < 1 || i > L.length + 1) return 0;     //i值不合法
    for(j = L.length; j > i; j++)               //向下移动元素,寻找插入位置
        L.data[j] = L.data[j-1];
    L.data[i-1] = x;                            //将新元素x放到插入的位置
    L.length++;                                 //顺序表长度增1

时间复杂度为O(n)。


  • 顺序表的删除
    算法 2.5
int ListDelete(SqList &L, int i) {             //在顺序表L中删除第i个元素
    int j;
    if(i < 1 || i > L.length) return 0;        //i值不合法
    for(j = i; j < L.length; j++)              //将结点i之后的元素前移一个位置
        L.data[j-1] = L.data[j];
    L.data

时间复杂度为O(n)。
- 顺序表的合并
先设C为空表,然后比较A和B中的数据元素,将A或B中较小的元素插入到C中。当其中一个顺序表的数据元素均已插入到C中时,另一顺序表中的剩余元素依次插入即可。
算法 2.6

void merge(SqList A, SqList B, SqList &C)
{
    int i = 0, j = 0, k = 0;                   //k记录顺序表C中的元素个数
    while(i < A.length && j < B.length)
    {
        if(A.data[i] < B.data[j])
        {
            C.data[k] = A.data[i];
            i++; k++;
        }
        else if(A.data[i] > B.data[j])
        {
            C.data[k] = B.data[j];
            j++; k++;
        }
        else                                   //A.data[i] = B.data[j]
        {
            C.data[k] = A.data[i];
            i++; k++;
            C.data[k] = B.data[j];
            j++; k++;
        }
    }
    while(i < A.length)                       //将A中剩余的元素复制到C中
    {
        C.data[k] = A.data[i];
        i++; k++;
    }
    while(j < B.length)                       //将B中剩余的元素复制到C中
    {
        C.data[k] = B.data[j];
        j++; k++;
    }
    C.length = k;                             //指定顺序表C的实际长度
}

时间复杂度为O(A.length + B.length)。

线性表的链式存储结构——链表

在此种存储结构中,线性表中的每一个元素以结点的形式存储。结点由数据域(存储数据元素的值)和指针域(存储数据元素的后继结点的地址)两部分构成。
avatar
在C语言中用“结构指针”来描述。

typedef struct node{
    ElemType data;
    struct node *next;
}SLink;

头结点:是链表的表头结点,其数据域为空,指针域指向链表的第一个结点(首结点),当链表为空时,指针域为空。
首结点:是链表的第一个结点,存放线性表的第一个数据元素。
头指针:是指向链表最前面结点的指针变量,即指向头结点,若表中无头结点,则指向首结点。如下图。
avatar

单链表

将线性表中数据元素的逻辑次序以结点的序列链接起来,并且只含有一个指针域的线性链表,称为单链表。
- 单链表的初始化
创建一个空的单链表,它只有一个头结点,由L指向它。该结点的next域为空,data域未设定任何值。
算法 2.7

void InitList(SLink *&L)                     //L为引用型参数
{
    L = (SLink * )malloc(sizeof(SLink));     //创建头结点 *L
    L->next = NULL;
}
  • 单链表的销毁
    单链表中的每个结点都是通过malloc函数分配的,不用时要通过free函数释放。
    实现过程是,先让pre指向头结点,p指向第一个数据结点,如下图,当p不为空时循环,释放pre所指结点空间,让pre、p指针沿next域同步后移一个结点。当循环结束,p为空,此时再释放pre所指的尾结点。
    avatar
    算法 2.8
void DestroyList(SLink *&L)
{
    SLink *pre = L, *p = pre->next;
    while(p != NULL)
    {
        free(pre);
        pre = p; p = p->next;                //pre、p同步后移
    }
    free(pre);
}

时间复杂度为O(n)。
- 单链表的查找
1)按序号查找(求线性表中第i个元素)
用p从头开始遍历单链表L中的结点,用计数器j累计遍历过的结点,其初值为0,当j等于i时,若p不为空,则p所指结点即为要找的结点,查找成功,算法返回1;否则算法返回0,表示未找到这样的结点。
算法 2.9

int GetElem(SLink *L, int i, ElemType &e)
{
    int j = 0;
    SLink *p = L;                            //p指向头结点,计数器j置为0
    if(i <= 0) return 0;                     //参数i错误返回0
    while(p != NULL && j < i)
    {
        j++;
        p = p->next;
    }
    if(p == NULL) return 0;                  //未找到返回0
    else
    {
        e = p->data;
        return 1;                            //找到后返回1
    }
}

时间复杂度为O(n)。
2)按值查找
算法 2.10

int Locate(SLink *L, ElemType e)
{
    SLink *p = L->next;
    int j = 1;                               //p指向第一个数据结点,j置为其序号1
    while(p != NULL && p->data != e)
    {
        p = p->next;
        j++;
    }
    if(p == NULL) return 0;                  //未找到返回0
    else return j;                           //找到后返回其序号
}

时间复杂度为O(n)。
- 在单链表中插入元素
在ai-1、ai之间插入x,即修改结点ai-1的指针域,令其指向结点x,而结点x中的指针域应指向结点ai
假设s为指向结点x的指针,p为指向结点ai-1的指针,则上述修改用语句描述为:s->next=p->next;p->next=s;如下图所示。
avatar
算法 2.11

int InsElem(SLink *&L, ElemType x, int i)   //插入结点值为x的结点
{
    int j = 0;
    SLink *p = L, *s;
    if(i <= 0) return 0;                    //参数i错误返回0
    while(p != NULL && j < i - 1)           //查找第i-1个结点*p
    {
        j++;
        p = p->next;
    }
    if(p == NULL)
        return 0;                          //未找到第i-1的结点时返回0
    else                                   //找到第i-1个结点 *p
    {
        s = (SLink * )malloc(sizeof(SLink));
        s->data = x;                       //创建存放元素x的新结点 *s
        s->next = p->next;                 //将 *s结点插入到 *p结点之后
        p->next = s;
        return 1;                          //插入运算成功,返回1
    }
}

时间复杂度为O(n)。
注意:插入操作的1和2执行顺序不能颠倒,否则若先执行p->next=s,由于修改p->next值,会使原 *p 结点的后继结点的地址丢失。
- 在单链表中删除元素
在带头结点的单链表L中,删除第i个位置元素。现在单链表L中查找第i-1个结点,若未找到返回0,找到后由p指向该结点,然后让q指向后继结点(即要删除的结点),若q所指结点为空则返回0,否则删除q所指结点,并释放其占用的空间。如下图。
在单链表中删除元素a<sub>i</sub>
算法 2.12

int DelElem(SLink *&L, int i)              //删除结点
{
    int j = 0;
    SLink *p = L, *q;
    if(i <= 0) return 0;                   //参数i错误返回0
    while(p != NULL && j < i - 1)          //查找第i-1个结点
    {
        j++;
        p = p->next;
    }
    if(p == NULL) return 0;                //未找到第i-1个结点时返回0
    else                                   //找到第i-1个结点 *p
    {
        q = p->next;                       //q指向被删结点
        if(q == NULL) return 0;            //没有底i个结点时返回0
        else
        {
            p->next = q->next;             //从单链表中删除 *q 结点
            free(q);                       //释放其空间
            return 1;
        }
    }
}

时间复杂度为O(n)。
- 创建单链表
假设给定一个含有n个元素的数组,由它来创建单链表。
该方法从一个空单链表(含有一个L指向的头结点)开始,读取数组a(含有n个元素)中的一个元素,生成一个新结点 *s,将读取的数据元素存放到新结点的数据域中,然后将新结点插入到当前链表的表尾上,如下图;再读取数组a的下一个元素,采用相同的操作建立新结点 *s,并插入到单链表L中,直到数组a中所有元素读完为止。
avatar
算法 2.13

void CreateListR(SLink *&L, ElemType a[], int n)
{
    SLink *s, *tc;
    int i;
    L = (SLink * )malloc(sizeof(SLink));       //创建头结点
    tc = L;                                    //tc始终指向尾结点,初始时指向头结点
    for(i = 0; i < n; i++)
    {
        s = (SLink * )malloc(sizeof(SLink));
        s->data = a[i];                        //创建存放a[i]元素的新结点 *s
        tc->next = s;
        tc = s;
    }
    tc->next = NULL;                           //尾结点next域置为NULL
}

时间复杂度为O(n)。
- 单链表的合并
设ha和hb分别是两个带头结点的有序递增单链表。将这两个单链表合并成一个包含所有数据点的新的有序递增单链表hc。要求hc仍使用ha和hb的存储空间,不另外占用其他的存储空间,ha和hb中允许有重复的数据。
算法思想:用pa遍历ha的数据结点,pb遍历hb的数据结点,将

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值