【数据结构】顺序表、单链表的 比较+总结

69 篇文章 0 订阅
32 篇文章 0 订阅

(优缺点文字部分转自: http://blog.csdn.net/zhang_minli/article/details/50489996

顺序表和链表de优缺点

顺序表的优点

方法简单,各种高级语言中都有数组,容易实现。
不用为表示结点间的逻辑关系而增加额外的存储开销(链表要增加额外的指针域)。
顺序表具有按元素序号随机访问的特点。

缺点
在顺序表中做插入删除操作时,平均移动大约表中一般的元素,因此对n较大的顺序表效率低。
需要预先分配足够大的存储空间,估计过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。
链表的优缺点恰好和顺序表相反。

链表和顺序表的插入、删除操作时间复杂度O(n)是两个不同的概念,链表O(n)主要是花费在查找上,顺序表O(n)主要花费在移动元素上,两者没有可比性。
在实际中应怎样选择存储结构?

1. 基于存储的考虑
顺序表在程序执行之前必须明确规定它的存储规模,也就是说事先对“MAXSIZE”要有合适的设定,过大造成浪费,过小造成溢出。可见对线性表的长度或存储规模难以估计时,不宜采用顺序表;
链表不用事先估计存储规模但链表的存储密度较低,顺序存储结构存储密度是1,链式存储结构的存储密度是小于1的。
2. 基于运算的考虑
在顺序表中按序号访问ai的时间性能是O(1),而链表中按序号访问的时间性能是O(n),所以如果经常做的运算是按序号访问数据元素,显然顺序表优于链表;而在循序表中做插入、删除操作时平均移动表中一半的元素,当数据元素的信息量较大且表较长时,这一点是不应忽视的;在链表中做插入、删除,虽然也要找插入位置,但操作主要是比较操作,从这个角度考虑显然链表优于顺序表。
3. 基于环境的考虑
顺序表容易实现,任何高级语言中都有数组类型,链表的操作是基于指针的,相对来讲前者简单些。

总之,顺序表和链表没有绝对的优劣之分,两种存储结构各有长短,关键看你看重什么,也就是要依据实际问题中的主要因素而定。通常“较稳定”的线性表选择顺序存储,而频繁做插入删除操作的,即动态性较强的线性表适合用链式存储。


下面是对顺序表、单链表的初始化、插入、寻找、删除、销毁、输出等的比较。

C版:

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1
#define Overflow 2
#define Underflow 3
#define NotPresent 4
#define Duplicate 5
typedef int Status;
typedef int ElemType;

typedef struct Node {
    ElemType val;
    Node* link;
}Node;

typedef struct {
    int maxLength;
    int n;
    ElemType* val;
}SeqList;

typedef struct  {
    Node* first;
    int n;
}SingleList;


typedef struct {
    Node* head;
    int n;
}HeaderList;

/*----------------Init()----------------*/
Status Init(SeqList* L, int mSize) {
    L->maxLength = mSize;
    L->n = 0;
    L->val = (ElemType*)malloc(sizeof(ElemType)*mSize);//生成一维数组空间
    if (!L->val) return ERROR;
    return OK;
}
Status Init(SingleList* singleList) {
    if (!singleList) return ERROR;
    singleList->first = NULL;
    singleList->n = 0;
    return OK;
}
Status Init(HeaderList* h)//因为要附加表头结点,故需为其开辟空间
{
    h->head = (Node*)malloc(sizeof(Node));
    if (!h->head) return ERROR;//有可能申请空间失败
    h->head->link = NULL;
    h->n = 0;
    return OK;
}
Status Init(Node* node) {
    if (!node) return ERROR;
    node->link = NULL;
    return OK;
}
/*----------------Find()----------------*/
Status Find(HeaderList h, int i, ElemType* x)
{
    Node* p;
    int j;
    if (i < 0 || i > h.n - 1) return ERROR;
    p = h.head;
    for (j = 0; j <= i; j++) {
        p = p->link;
    }
    *x = p->val;
    return OK;
}

Status Find(SingleList L, int i, ElemType* x) {
    if (i<0 || i>L.n - 1) return ERROR;
    Node* p;
    p = L.first;//p指向下标0处
    for (int j = 0; j < i; j++)  p = p->link;//p走了i步--》两句一共走到了下标i处
    *x = p->val;
    return OK;
}
Status Find(SeqList L, int i, ElemType* x) {
    if (i<0 || i>L.n - 1) return ERROR;
    *x = L.val[i];
    return OK;
}

/*----------------Insert()----------------*/
Status Insert(HeaderList* h, int i, ElemType x)
{
    Node *pCur, *pNext;
    int j;
    if (i < -1 || i > h->n - 1) return ERROR;//注意:是i < -1 ,虽然已定义表头结点,但是这与下标i的取值无关!i照样可以取-1,表示插在最前面!

    pCur = h->head;
    for (j = 0; j <= i; j++) {//“=”勿忘!!!
                              //若传入的i为-1(插入表首元素位置),则该循环不执行。pCur指向表头结点,将pNext->link与pCur->link(原首个元素)相连。
                              //注意:i为0时(插入表的第二个元素位置),需要将pCur后移一个,使pCur指向原首个元素。以便后面将pNext连接到pCur后面(原第二个元素)。
        pCur = pCur->link;
    }
    //pNext = (Node*)malloc(sizeof(Node));
    pNext = new Node;

    pNext->val = x;
    pNext->link = pCur->link;
    pCur->link = pNext;

    h->n++;
    return OK;
}

Status Insert(SingleList* L, int i, ElemType x) {//多半将pNext插入到pCur之后,除非i=-1,插到最前面。
    Node* pCur, *pNext;
    if (i<-1 || i>L->n - 1) return ERROR;
    pCur = L->first;
    for (int j = 0; j < i; j++) {
        pCur = pCur->link;
    }
    //创建要插入的结点(申请内存空间)
    pNext = (Node*)malloc(sizeof(Node));//复制x值到新Node,需要先为其开辟空间。
    pNext->val = x;
    //插入链表
    if (i > -1) {
        pNext->link = pCur;
        pCur->link = pNext;
    }
    else {// i = -1
        pNext->link = L->first;
        L->first = pNext;
    }
    L->n++;
    return OK;
}

Status Insert(SeqList* L, int i,ElemType x) {
    if (i<0 || i > L->n - 1) return ERROR;
    if (L->n == L->maxLength) return ERROR;//顺序表独有!
    for (int j = L->n - 1; j > i; j--) {
        L->val[j + 1] = L->val[j];
    }
    L->val[i + 1] = x;
    L->n++;
    return OK;
}

/*----------------Delete()----------------*/
Status Delete(HeaderList* h, int i)
{
    Node *pCur, *pPre;//pCur:被删者,pPre:被删者的前驱结点
    if (!h->n /*|| !h->head*/) return ERROR;
    if (i < 0 || i > h->n - 1) return ERROR;
    pPre = h->head;
    for (int j = 0; j < i; j++) {//注意:此处不加"="
                                 //若i=0,即删除首元素(pCur),无需将pPre后移(此时pPre指向表头):直接将pCur与pPre->link(首个元素)相连。释放pCur。
                                 //若i=1,即删除第二个元素,需要将pPre后移一位,指向第一个元素,然后将pCur指向pPre->link(第二个元素)。释放pCur。
        pPre = pPre->link;
    }

    pCur = pPre->link;
    pPre->link = pCur->link;

    free(pCur);
    h->n--;
    return OK;
}

Status Delete(SingleList* L, int i)
{
    Node *pCur, *pPre;//pCur:被删者,pPre:被删者的前驱结点
    if (!L->n /*|| !h->head*/) return ERROR;
    if (i < 0 || i > L->n - 1) return ERROR;

    pCur = L->first;
    pPre = L->first;//pPre走到了下标0处
    for (int j = 0; j < i - 1; j++) {//i为0或1时不移动。否则,将pPre移动i-1次,两句j一共移动到下标i-2(+1)处,即i-1处
        //object:找到被删者i的前驱结点√
        pPre = pPre->link;
    }

    if (i == 0) L->first = L->first->link;
    else {
        pCur = pPre->link;//通过其前驱节点pPre,将被删者pCur指向更新,以便后面删除pCur
        pPre->link = pCur->link;
    }
    free(pCur);
    L->n--;
    return OK;
}

Status Delete(SeqList* L, int i) {
    int j;
    if (i<0 || i>L->n - 1) return ERROR;
    if (!L->n) return ERROR;
    for (j = i + 1; j <= L->n - 1; j++) {
        L->val[j - 1] = L->val[j];
    }
    L->n--;
    return OK;
}
/*----------------Output()----------------*/
Status OutPut(HeaderList h)
{
    Node *pCur;
    if (!h.n) return ERROR;

    Version 1
    //pCur = h.head;//注意此处若加了link,则需先打印,再后移pCur
    //while (pCur->link)
    //{
    //  pCur = pCur->link;//①保证了头结点的val不打印(因为无意义)
    //                    //②(遍历到倒数第二个元素时)保证了循环体内可以打印出最后一个元素
    //  printf("%d ", pCur->val);
    //}

    //Version 2 & 3
    pCur = h.head->link;//保证了头结点的val不打印(因为无意义)。注意此处加了link,需先打印元素,再后移pCur
    while (pCur)//循环条件若为pCur->link,则循环结束后,最后一个元素还需打印
    {
        printf("%d ", pCur->val);
        pCur = pCur->link;
    }
    //printf("%d ", pCur->val);//循环条件若为pCur->link,为打印出最后一个元素,需要在循环体外加上此句。
    printf("\n");
    return OK;
}

Status Output(SingleList L) {
    if (!L.n) return ERROR;
    Node* p = L.first;
    while (p)
    {
        printf("%d ", p->val);//此处可看出 first不是表头结点,仅仅是SingleList的头结点。无dummy作用。
        p = p->link;
    }
    return OK;
}
Status Output(SeqList L) {
    if (!L.n) return ERROR;
    for (int i = 0; i <= L.n - 1; i++) {
        printf("%d ", L.val[i]);
    }
    return OK;
}

Status OutPut(Node* node)
{
    Node* p;
    if (/*!L.val ||*/ !node->link) return ERROR;
    p = node/*->link*/;
    while (p->link)//若不判断p->link,则最后一个p无后继结点,打印最后一个p的元素后,p继续后移,之后本应退出循环却未退出,导致结尾多打印出了一个’?’
    {
        printf("%d ", p->val);
        p = p->link;
    }
    printf("\n");
    return OK;
}


Status OutputNode(Node* node)
{
    Node* p;
    //    if (/*!L.val ||*/ !node->val) return ERROR;
    p = node;
    while (p)//若不判断p->link,则最后一个p无后继结点,打印最后一个p的元素后,p继续后移,之后本应退出循环却未退出,导致结尾多打印出了一个’?’
    {
        printf("%d ", p->val);
        p = p->link;
    }
    printf("\n");
    return OK;
}

/*----------------Destory()----------------*/
void Destroy(HeaderList* h)
{
    Node* temp;
    Node* head = h->head;
    (*h).n = 0;
    while (head) {
        temp = head->link;
        free(head);
        head = temp;
    }
}
void Destroy(SingleList* L)
{
    Node* temp;
    Node* head = L->first;
    L->n = 0;
    while (head) {
        temp = head->link;
        free(head);
        head = temp;
    }
}

void Destroy(Node* node)
{
    Node* temp;
    while (node) {
        temp = node->link;
        free(node);
        node = temp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值