线性表

#线性表#的存储结构包括:

顺序表

链表

链表的五种形式:

  • 单链表

    1. 带头节点:head->next ==NULL
    2. 不带头节点:head == NULL

  • 双链表

    1. 带头节点:head->next ==NULL
    2. 不带头节点:head == NULL

  • 循环单链表

    1. 带头节点:head == head->next
    2. 不带头节点:head == NULL

  • 循环双链表

    1. 带头节点:检查head->priorhead->next两者中的任意一个是否等于head即可
    2. 不带头节点:head == NULL

  • 静态链表:借助一维数组来表示

顺序表和链表的区别

  • 基于空间:

    1. 顺序表的存储空间是一次性分配,链表的存储空间是动态分配的;
    2. 顺序表的存储密度=1,而链表的存储密度<1。

  • 基于时间:

    1. 顺序表能随机存取,而链表只能顺序存取;
    2. 顺序表插入删除时几乎需要移动一半的元素::时间复杂度为O(n)::,而链表不需要移动元素,只需要修改指针即可。

定义顺序表

int A[maxSize];
int n;
//或者这样写
typedef struct Sqlist
{
    int data[maxSize];
    int length;
}Sqlist;

定义链表

  1. 单链表
typedef  struct  LNode
{
    int data;
    struct LNode* next;
}LNode;
  1. 双链表
typedef  struct  DLNode
{
    struct DLNode* prior;
    int data;
    struct DLNode* next;
}DLNode;

初始化顺序表时只需要将长度设置为0便可

习题

  1. 顺序表用数组[m+n]A表示,前m个元素递增有序,后n个元素递增有序,设计一个算法,使得整个顺序表有序。
void insertElem(int A[],int m,int n)
{
    int temp;
    int i,j;
    for(i = m ; i < m+n ; i++)
    {
        temp = A[i];
        for(j = i-1 ; j >= 0 && temp < A[j] ; j--)
            A[j+1] = A[j];//此时j位置有个坑,指针在j位置
            //此刻指针跳到j-1位置
        //此刻,j元素<=temp,j+1元素> temp
        A[j+1] = temp;
    }
}

::时间复杂度为O(mn)::

::空间复杂度为O(1),算法所需的额外存储空间与数据规模无关!::

  1. 两个递增有序的单链表A、B,元素个数分别为m和n,都有头结点。求A-B,结果放在A链表里面。
void difference (LNode* A,LNode* B)
{
    LNode * p = A->next;
    LNode * q = B->next;
    LNode * prior = A;
    while(p != NULL && q !=NULL)
    {
        if(p->data < q->data)
        {
            prior = p;
            p = p->next;//只移动p及其前缀
        }
        else if(p->data > q->data)
        {
            q = q->next;//只移动q
        }
        else
        {
            pre->next = p->next;
            free(p);
            p = pre->next;
        }
    }
}

::时间复杂度为O(m+n)::

  1. 为什么在单循环链表中设置尾指针比设置头指针更好?
    ::因为可以用它来方便的查找链表的开始结点和终端结点,查找时间均为O(1)::

  2. 有一个顺序表L,全是整型数据,设计一个算法,将L中所有小于表头元素的整数放在前半部分,大于表头元素的帧数放在后半部分。

void move (Sqlist& L){
    int temp;
    int i = 0;
    int j = L.length-1;
    temp = L.data[i];
    while(i<j){
        while(i<j && L.data[j]>temp)
        j--;
        if(i<j){
            L.data[i] = L.data[j];
            i++;
        }
        while(i<j && L.data[i]<=temp)//加一个等号,结果大不一样!
        i++;
        if(i<j){
            L.data[j] = L.data[i];
            j--;
        }
    }
    L.data[i] = temp;
}  
  1. 有一个递增非空链表,设计一个算法删除值重复的结点。
void deletelist(LNode* L){
    LNode *p = L->next;
    LNode *q = p->next;
    LNode *r;
    while(q != NULL){ //q从头到尾遍历一遍链表,找不同
        while(q != NULL && q->data == p->data) q = q->next;
        if(q != NULL){
            p = p->next;
            p->data = q->data;
        } 
    }
    q = p->next;
    p->next = NULL; //将有用部分和重复部分斩断联系
    while(q != NULL){ //删除重复的部分
        r = q;
        q = q->next;
        free(r);
    }
}
  1. 设计一个算法删除单链表L(有头结点)中的一个最小值结点。
void deletemin(LNode *L){
    LNode *pre = L, *p = L->next, *min = p, *minpre = pre;
    while(p != NULL){
        if(p->data < min->data){
            min = p;
            minpre = pre;
        }
        p = p->next;
        pre = pre->next;
    }
    minpre->next = min->next;
    free(min);
}

::遍历链表需要两指针,删除一结点需要两指针,一共需要4指针::

反之,如果要删除最大的结点呢?::也得要4个指针。::

  1. 设计一个算法来逆置一个带头结点的单链表。
void reverse(LNode *L){
    //利用头插法,可以使链表逆序
    LNode *p = L->next;
    LNode *q;
    L->next = NULL;
    while(p != NULL){
        q = p->next;
        p->next = L->next;
        L->next = p;
        p = q;
    }
}
  1. 设计一个算法,逆序打印单链表数据。
void reprint(LNode *L){
    if(L != NULL){
        reprint(L->next);
        cout<<L->data<<" ";
    }
    cout<<endl;
}
  1. 设计一个算法,输出带头结点的单链表的倒数第k个结点的值。
void kvalue(LNode *L, int k){
    LNode *p = L->next;
    LNode *q = L;
    int i = 1;
    while(p != NULL){
        p = p->next;
        i++;
        if(i > k)
        q = q->next; 
    }
    if(q = L)
    cout<<0;
    else
    cout<<q->data;
}
原文地址:https://www.cnblogs.com/endymion/p/9090414.html