链表基础

1.链表操作

删除、插入、查找、排序、合并、复制

单链表的操作

GelElem(Lnode *head,int i): 找到第i个元素。(必须从头指针出发寻找,复杂度为O(n))

Insert(Lnode *head,int i,ElemType e): 在位置i之前插入元素e。(对于线性表的非线性存储结构,在已知链表中元素的插入或删除的确切位置的情况下,在单链表中插入或者删除一个节点时,只需要修改指针而不需要移动元素)(必须找到第i-1个节点,复杂度为O(n))

Delete(Lnode *head,int i):删除第i个节点。(必须找到第i-1个节点,复杂度为O(n))

remove(Lnode *head,Lnode *ToBeRemoved): 删除某一节点。(对于非尾结点,复杂度为O(1),尾结点的复杂度为O(n))

length(struct Lnode*head):求长度。(从头遍历,复杂度为O(n))

MergeList(Lnode *a,Lnode *b,Lnode *c):将2个有序链表合并为一个有序链表。(时间复杂度?空间复杂度?)

删除操作的说明:

(1)单向链表。

对于带头结点的单向列表,分两种情况讨论,如果待删除的不是尾结点,则直接将后面的结点往前复制;如果待删除的是尾结点,则需要从头开始找到尾结点前面的一个结点,将尾结点的next指针置空。可以在O(1)复杂度下删除某一非尾部结点,删除尾部结点时复杂度为O(n)。 

循环单向链表的操作与线性单向链表基本一致,差别仅仅在于算法中的循环条件不是p或者p->next是否为空,而是它们是否为头指针。

但是在循环列表中仅设尾指针而不设头指针时,可以将某些操作简单化,比如将2个线性表合并时,仅需将一个表的表尾和另一个表的表头相连接,运算时间为O(1)。 

 

2.存储结构

线性存储;(数组存储,插入或者删除时需要移动数组)

静态链表;(也用数组表示,其中一个分量表示结点,另一个分量代替指针来表示结点在数组中的相对位置。插入或者删除时不需要移动元素,仍具有链式结构存储的主要优点)

单链表:不带表头结点的单链表,head指向首结点,当head==NUll,为空表,否则为非空表;

           带表头结点的单链表,head指向头结点,data为null,pnext指向首结点,当head->pNext==NULL时为空表。

#include"stdlib.h"    //或"malloc.h",动态存储分配头文件
#include"stdio.h"     //scanf需要的头文件
#include <iostream>
using namespace std;

#define NULL 0                       //定义空指针NULL
struct Lnode          //Lnode为结点(结构)类型
{  int data;          //假定data为整型
   struct Lnode *next;//next为指针类型
};

#define LENG sizeof(struct Lnode)    //结点所占的字节数

//生成带表头结点的单链表。一定以0作为末尾结点
struct Lnode*createl()
{
    struct Lnode *head,*tail,*p;
    int e;
    head=(struct Lnode*)malloc(LENG);    //新建这样一个结点时,data随机,next也是一个随机的地址值
    tail=head;
    do 
    {
        p=(Lnode*)malloc(LENG);
        scanf("%d",&e);
        p->data=e;
        tail->next=p;
        tail=p;
    } while (e);    //在检查条件是否为真时,首先执行一次代码块。这里e不为0时继续
    tail->next=NULL;     //使得尾指针为空。当链表为空时,head->next=NULL
    return head;
}

//打印带表头的单链表
void printl(Lnode *head)
    {
        struct Lnode* ToBePrint;
        ToBePrint=head->next;
        while (ToBePrint!=NULL)
        {    
             cout<<ToBePrint->data;
            ToBePrint=ToBePrint->next;
         } 
}


void main()
{
    Lnode* head=createl();
    printl(head);
}            
//删除单向列表的结点
void remove(Lnode *head,Lnode *ToBeRemoved)
{
    if(!head||!ToBeRemoved||head->next==NULL||ToBeRemoved==head)  // 空表||要删除的是head结点
        return;
    if(ToBeRemoved->next!=NULL)    //要删除的不是尾结点。如果要删除的是结点i
    {
        Lnode *p=ToBeRemoved->next;
        ToBeRemoved->data=p->data;
        ToBeRemoved->next=p->next;
        free(p);   //和malloc对应
        p=NULL;
    }
    else   //要删除的是尾结点(包括只有一个结点的情况),必须找到尾结点以前的结点
    {
        Lnode *p=head;
        while(p->next!=ToBeRemoved)
        {
            p=p->next;
        }
        p->next=NULL;
        free(ToBeRemoved);
        ToBeRemoved=NULL;
    }
}

//找到带头结点的单向列表的第k个结点
Lnode* find(Lnode* head,int k)
{
    if(k==0||head->next==NULL)
        return NULL;
    int count=1;
    Lnode* r=head->next;
    while(count<k)
    {
        if(r->next==NULL)   //已经是尾结点了,但是仍然没有找到第k个结点
            return NULL;
        r=r->next;
        count++;
    }
    return r;
}
//求带头结点的线性链表的长度,并依次输出结点的值
int length(struct Lnode*head)
{
    int leng=0;
    Lnode* p;
    p=head->next;   //p指向首结点
    while(p!=NULL)  //p非空
    {
        //cout<<p->data;
        leng++;
        p=p->next;
    }
    return leng;
}

 循环链表:

循环链表在生成时唯一与单链表不同的就是尾指针指向了头结点

(1)带头结点的循环链表:

非空:H->next≠H, H≠NULL

空:H->next==H, H≠NULL

(2)只设尾指针的循环链表:

非空:tail 指向尾结点,tail->data==an

    tail->next 指向表头结点

    tail->next->next指向首结点

    tail->next->next->data==a1

空:tail->next==tail

 双向链表:

(1)非空表:L为头指针,L指向表头结点,L->next指向首结点

       L->next->data==a1

       L->prior指向尾结点

       L->prior->data==an

       L->next->prior== L->prior->next==L

(2)空表:L->next==L->prior==NULL

双向循环链表:

(1)空表:L->next==L->prior==L

(2)非空表:设p指向a1,  则:

p->next指向a2 , p->next->prior指向a1

有:  p==p->next->prior, p==p->prior->next

题目:删除双向链表中的某个结点

 

 

p->prior->next = p->next;  //结点A的next指向结点C

p->next->prior = p->prior; //结点C的prior指向结点A

free(p)

(1)对于非循环的双向链表:

题目:双向链表中插入结点

 ① f->prior=p->prior;  //结点B的prior指向结点A

   ② f->next=p;          //结点B的next指向结点C

   ③ p->prior->next=f;   //结点A的next指向结点B

   ④ p->prior=f;         //结点C的prior指向结点B

 

3.思考

(1).线性表的顺序存储结构有什么优点和缺点?

线性表的顺序存储

 优点: 具有简单、运算方便等优点,特别是对于小线性表或长度固定的线性表,采用顺序存储结构的优越性更为突出;

 缺点:1.顺序存储插入与删除一个元素,必须移动大了的数据元素,以此对大的线性表,特别是在元素的插入和删除很频繁的情况下,采取顺序存储很是不方便,效率低;

         2.顺序存储空间容易满,出现上溢,程序访问容易出问题,顺序存储结构下,存储空间不便扩充;

         3.顺序存储空间的分配问题,分多了浪费,分少了空间不足上溢。

对于大的线性表,特别是元素变动频繁的大线性表不宜采用顺序存储空间,而采用链式存储结构。

(2).试比较单链表、双链表、循环链表的优、缺点。

单链表:如果访问任意结点每次只能从头到尾顺序向后访问
单循环链表:可以从任何一个结点开始,顺序向后访问到达任意结点
双向链表:可以从任何结点开始任意向前向后双向访问操作
单链表和单循环链表:只能在当前结点后插入和删除
双链表:可以在当前结点前面或者后面插入,可以删除前趋和后继(包括结点自己)
存储:单链表和单循环链表存储密度大于双链表

posted @ 2015-08-22 13:59  wy1290939507  阅读(301)  评论(0编辑  收藏  举报