数据结构:线性表

2.1 线性表及逻辑结构

2.1.1 线性表的定义

  • 线性表是具有相同特性的数据元素的一个有限序列

线性表有如下几条性质:

  • 有穷性:一个线性表中的元素是有限的
  • 一致性:一个线性表中的所有元素性质相同,从现实角度看,每个元素具有相同的数据类型
  • 序列性:一个线性表中所有元素之间的相对位置是线性的,即存在唯一的开始元素和终端元素,除此之外,每个元素之有唯一的前驱元素和后继元素。各元素在线性表中的位置只取决于它们的序号,所以一个线性表中可以存在两个值相同的元素。

2.1.2 线性表的抽象数据类型描述

ADT List{

数据对象: D={ai| ai(-ElemSet,i=1,2,…,n,n>=0}

数据关系: R1={<ai-1,ai>| ai-1,ai(- D,i=2,…,n}

基本操作:

1.InitList(&L)
操作结果:构造一个空的线性表

2.DestroyList(&L)
初始条件:线性表L已存在
操作结果:销毁线性表L

3.ClearList(&L)
初始条件:线性表L已存在
操作结果:将L重置为空表

4.ListEmpty(L)
初始条件:线性表L已存在
操作结果:若L为空表,则返回TRUE,否则返回FALSE

5.ListLength(L)
初始条件:线性表L已存在
操作结果:返回L中数据元素个数

6.GetElem(L,i,&e)
初始条件:线性表L已存在,1≤i≤LIST_SIZE
操作结果:用e返回L中第i个数据元素的值

7.LocatElem(L,e,compare())
初始条件:线性表L已存在,compaer()是数据元素判断函数
操作结果:返回L中第1个与e满足关系compaer()的数据元素的位序。若这样的数据元素不存在,则返回0

8.ListInsert(&L,i,e)
初始条件:线性表L已存在
操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1

9.ListDelete(&L,i,&e)
初始条件:线性表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1

}ADT List

2.2 线性表的顺序存储结构

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

线性表的顺序存储结构称为顺序表

2.2.2 顺序表基本算法实现

#include <iostream>
using namespace std;
const int maxsize =  100;

typedef int ElemType;
typedef struct{
    ElemType data[maxsize];
    int length;
}Sqlist;

void CreateList(Sqlist *& L,ElemType a[],int n){
    int i;
    L = new Sqlist;
    for(i = 0;i < n;i++)
        L->data[i] = a[i];
    L->length = n;
}
void InitList(Sqlist *& L){
    L = new Sqlist;
    L->length = 0;
} 
void DestroyList(Sqlist * &L){
    L -> length = 0;
    delete [] L;
}
bool ListEmpty(Sqlist *L){
    return (L -> length == 0);
}
int ListLength(Sqlist *L){
    return L -> length;
}
void DispList(Sqlist *L){
    int i;
    for(i = 0;i < ListLength(L);i++)
        cout << L -> data[i] << " ";
    cout << endl;
}
bool GetElem(Sqlist *L,int i,ElemType &e){
    if(i < 0||i > L->length)
        return false;
    e = L -> data[i-1];
    return true;
}
int LocateElem(Sqlist *L,ElemType e){
    int i;
    for(i = 0;i < L->length;i++){
        if(L->data[i] == e) return i+1;
    }
    return -1;
}
bool ListInsert(Sqlist *L,int i,ElemType e){
    if(i < 1||i > L->length+1) return false;
    else
    {
        i--;
        int j;
        for(j = L->length;j > i;j--){
            L->data[j] = L->data[j-1]; 
        }
        L->data[i] = e;
        L->length++;
        return true;
    }
}
bool ListDelete(Sqlist *L,int i,int &e){
    if(i < 1 || i > L->length)  return false;
    else
    {
        i--;
        e = L->data[i];
        for(int j = i;j < L->length - 1;j++)
            L->data[j] = L->data[j+1];
        L->length--;
        return true;
    }
    
}
int main(){
    Sqlist * node;
    ElemType data[maxsize];
    int n;
    cout << "输入初始化顺序表长度" << endl;
    cin >> n;cout << "输入顺序表值" << endl;
    for(int i = 0;i < n;i++)
        cin >> data[i];
    CreateList(node,data,n);
    cout << "顺序表元素为 " << endl;
    DispList(node);
    cout << "顺序表长为 " << ListLength(node) << endl;
    
    cout << "输入插入元素位置和数值" << endl;
    int p,e;cin >> p >> e;
    if(ListInsert(node,p,e)){
        cout << "插入后为" << endl;
        DispList(node);
    }
    else
    {
        cout << "输入不合法" << endl;
    }
    
    cout << "删除插入位置后顺序表为" << endl;
    ListDelete(node,p,e);
    DispList(node);
    
    cout << "现在销毁顺序表" << endl;
    DestroyList(node);

    if(ListEmpty(node)) cout << "现在为空表"  << endl;
    else
    {
        cout << "现在仍存在顺序表" << endl;
    }
    return 0;
}

3 顺序表的应用示例

例2.3 假设一个线性表采用顺序表表示,设计一个算法,删除其中所有值等于x的元素,要求算法的时间复杂度是O(n),空间复杂度是O(1)

代码实现:

解法一

void delnode1(Sqlist * & L,ElemType x){
    int i,k = 0;
    for(i = 0;i < L->length;i++){
        if(L->data[i] == x)
            k++;
        else{
            L->data[i-k] = L->data[i];
        }
    }
    L->length -= k;
}

解法二

void delnode2(Sqlist *L,ElemType x){
    int i,k = 0;
    for(i = 0;i < L->length;i++){
        if(L->data[i] != x){
            L->data[k] = L->data[i];
            k++;
        }
    }
    L->length = k;
}

例2.4 有一个顺序表L,假设元素类型ElemType为整型,设计一个尽可能高效的算法,以第一个元素为分界线,将所有小于等于它的元素移动到他的元素该基准的后面,将所有大于它的元素移动到该基准的前面。

基本思路是以第一个元素为基准,从右向左找一个小于等于基准的元素x,从左向右找到一个大于基准的元素y,然后将两者交换,直到全部找完.
代码实现:

解法一

void partition1(Sqlist * & L){
    int i = 0,j = L->length-1;
    ElemType pivot = L->data[0];
    while(i < j){
        while(i < j&&L->data[j] > pivot)
            j--;
        while(i < j&&L->data[i] <= pivot)
            i++;
        if(i < j) swap(L->data[i],L->data[j]);
    }
    swap(L->data[0],L->data[i]);
}

解法二

void partition2(Sqlist *&L){
    int i = 0,j = L->length-1;
    ElemType pivot = L->data[0];
    while(i < j){
        while(i < j && L->data[j] > pivot)
            j--;
        L->data[i] = L->data[j];
        while(i < j && L->data[i] <= pivot)
            i++;
        L->data[j] = L->data[i];
    }
    L->data[i] = pivot;
}

尽管对一个同一个数据序列,这两个算法的执行结果不完全相同,但是都能满足题目要求,而且它们的时间复杂度都是O(n),空间复杂度为O(1),都属于高效算法
但是比较而言,第二个算法移动元素的次数更少,所以算法更优.

2.3 线性表的链式存储结构

2.3.1 线性表的链式存储结构–链表

线性表的链式存储结构称为链表(linked list),其中每个存储节点不仅包含元素本身信息(称为数据域),而且包含表示元素之间逻辑关系,在c/c++通过指针来实现,这称为指针域

2.3.2 单链表

1 . 结点声明

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LinkNode;

为了整体替换int数据类型增加如下表达

typedef int ElemType;

2.单链表的一些基本操作

2.1 建立单链表

头插法:在头结点后依次插入元素(若输出数据会呈现出输入的倒叙)

void CreatListF(LinkNode *& L,ElemType a[],int n)  //头插法 逆序输入
{
    LinkNode *s;
    L = new LinkNode;
    L->data = n;L->next = NULL;
    for(int i = 0;i < n;i++){
        s = new LinkNode;
        s->next = L->next;
        L->next = s;
        s->data = a[i];
    }
}

尾插法:在尾部依次插入元素(输出则为正常顺序)

void CreatListR(LinkNode *& L,ElemType a[],int n) //尾插法
{
    LinkNode *s,*r;
    L = new LinkNode;
    L->data = n;L->next = NULL;
    r = L;//r永远指向尾节点
    for(int i = 0;i < n;i++){
        s = new LinkNode;
        r->next = s;
        r = s;
        s->data = a[i];
    }
    r->next = NULL;
}

2.2 初始化单链表

void InitList(LinkNode *& L)
{
    L = new LinkNode;
    L->next = NULL;
}

2.3 销毁单链表

void DestroyList(LinkNode *& L)
{
    LinkNode *pre = L,*p = L->next;
    while(p != NULL)
    {
        delete pre;
        pre = p;
        p = p->next;
    }
}

2.4 判断是否为空表

bool ListEmpty(LinkNode *L){
    return (L->next == NULL);
}

2.5 求链表长度

int ListLength(LinkNode *L){
    LinkNode *p = L->next;
    int n = 0;
    while(p){
        n++;
        p = p->next;
    }
    return n;
}

2.6 输出线性表

void DispList(LinkNode *L){
    LinkNode *p = L->next;
    while(p){
        cout << p->data << " ";
        p = p->next;
    }
    cout << endl;
}

2.7 求单链表中某个逻辑位置上数据元素的值

bool GetElem(LinkNode *L,int i,int &e){
    if(i < 1) return false;   
    else
    { 
        int j = 0;
        LinkNode *p = L->next;
        i--;
        while(i > j && p!= NULL){
            j++;
            p = p->next;
        }
        if(p == NULL) return false;
        else
        {
            e = p->data;
            return true;
        }
    }
}

2.8 按元素e查找,返回元素逻辑序号

int LocateElem(LinkNode * L,ElemType e){
    LinkNode *p = L->next;
    int k = 1;
    while(p != NULL && p->data != e){
        p = p->next;
        k++;
    }
    if(p == NULL) return -1;
    else
        return k;
}

2.9 插入数据元素 在第i个位置上 插入元素e

bool ListInsert(LinkNode *& L,int i,ElemType e){
    if(i < 1) return false;
    LinkNode *p = L,*s;
    for(int j = 1;j < i;j++){
        if(!p) return false;
        p = p->next;
    }
    s = new LinkNode;
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}

2.10 删除第i个位置上的元素 并返回删除元素的值

bool DeleteList(LinkNode *&L,int i,ElemType &e){
    if(i < 1) return false;
    else
    {
        LinkNode *p = L,*q;
        for(int j = 1;j < i;j++){
            if(!p) return false;
            else
                p = p->next;
        }
        q = p->next;
        e = q->data;
        p->next = q->next;
        delete q;
    }
    return true;
}

讲完单链表的基本操作,现在我们来看单链表的应用示例

例子

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值