数据结构 线性表的链式实现

471 阅读10分钟

链表的概念

链表.jpg
(1)单链表:当链表中的每个结点只含有一个指针域时,称为单链表。

(2)头指针:如上图所示,链表中第一个结点的存储位置叫做头指针。

(3)头结点:头结点是放在第一个元素结点之前的结点,头结点不是链表中的必须元素,其数据域一般无意义,(有些情况下会存放链表的长度,或用作监视哨等),本文的头结点存放了链表的长度。有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。

(4)首元结点:就是第一个元素的结点,它是头结点后边的第一个结点。

单链表的基本操作

(1)初始化:给链表添加一个头结点。

(2)逆置链表:将单链表所有元素逆置。

(3)链表遍历:打印出链表的长度,并按顺序打印所有元素。

(4)查找:获取指定位置的元素。

(5)元素插入:在指定位置插入元素。

(6)元素删除:删除指定位置的元素。

(7)删除链表:将链表删除。

(8)头插法创建链表:用头插法插入一系列元素,创建链表。

(9)尾插法创建链表:用尾插法插入一系列元素,创建链表。

(10)快慢指针:利用快慢指针法的原理,快速查找一个长度未知的链表的中间结点。

这些操作中,链表逆置具有一定的难度,其基本的设计思想是:将以前的旧链表中的每个元素从旧链表中删除,并用头插法插入新的链表中。

快慢指针的设计也非常巧妙,快指针运动速度是慢指针的两倍,因此快指针指向最后一个结点的时候,慢指针刚好指向中间结点。

线性表的链式存储结构的特点

用一组任意的存储单元存储线性表的数据元素(单元可以是连续的,也可以是不连续的)

结点(node)

结点包括两个域

**数据域(data)**存储数据元素信息的域

**指针域(next)**存储直接后继存储位置的域,指针域中存储的信息称为指针或链

单链表编程实现----C语言

在C语言中函数和数据是分开的,每个函数都需要考虑链表的数据传递问题,这大大加大了编程的难度。 (1)链表的头文件 linklist.h

#ifndef LINKLIST_H
#define LINKLIST_H
 
typedef int ElemType;
typedef struct Node
{
    ElemType data;
    struct Node *Next;
}Node;
 
typedef Node* linklist;//给节点指针取别名
typedef enum Bool
{
    FALSE,TRUE//枚举默认值从0开始,依次加1
}Bool;
 
Bool InitList(linklist* L);//初始化链表,给链表添加一个头结点
Bool GetElem(linklist L, int i, ElemType* e);//获得第i个位置的元素
Bool ListInsert(linklist* L,int i,ElemType e);//在第i个位置插入元素
Bool ListDelete(linklist* L,int i,ElemType* e);//删除第i个元素
Bool ListDeleteAll(linklist* L);//删除全部链表
void traverseList(linklist L);//遍历链表
void CreateListHead(linklist* L,int n);//头插法创建链表
void CreateListEnd(linklist* L,int n);//尾插法创建链表
void Reverse(linklist* L);//逆置链表
int findMiddle(linklist L, ElemType* e);//查找中间结点,将查找的元素值赋给e,并返回该节点是第几个结点,如果是空表则返回0
 
#endif // LINKLIST_H

(2)链表的源文件linklist.c

#include "linklist.h"
#include <stdio.h>
 
Bool GetElem(linklist L, int i, ElemType* e)
{
    linklist p=L;
    int j;
    for(j=1;j<=i;j++)
    {
        p=p->Next;
        if(p==NULL)
            return FALSE;
    }
    *e=p->data;
    return TRUE;
}
 
Bool ListInsert(linklist* L,int i,ElemType e)
{
    linklist p=*L,s;
 
    int j;
    for(j=1;j<i;j++)
    {
        p=p->Next;
        if(p==NULL)
            return FALSE;
    }
    s=(linklist)malloc(sizeof(Node));
    s->data=e;
    s->Next=p->Next;
    p->Next=s;
    (*L)->data++;
    return TRUE;
}
 
Bool ListDelete(linklist* L,int i,ElemType* e)
{
    linklist p=*L,q;
    int j;
    for(j=1;j<i;j++)
    {
        p=p->Next;
        if(p==NULL)
            return FALSE;
    }
 
    q=p->Next;
    p->Next=q->Next;
    (*L)->data--;
 
    *e=q->data;
    free(q);
    return TRUE;
}
 
Bool InitList(linklist* L)
{
    *L=(linklist)malloc(sizeof(Node));
    (*L)->data=0;
    (*L)->Next=NULL;
    return TRUE;
}
 
void traverseList(linklist L)
{
    linklist p=L;
    printf("链表总长度为:%d  \n",p->data);
    p=p->Next;
    while(p)
    {
        printf("%d  ",p->data);
        p=p->Next;
    }
    printf("\n\n");
//    printf("display ok!\n");
}
 
void CreateListHead(linklist* L,int n)
{
    linklist p;
    int i;
 
    for(i=0;i<n;i++)
    {
        p=(linklist)malloc(sizeof(Node));
        p->data=i;//rand()%100+1;
        p->Next=(*L)->Next;
        (*L)->Next=p;
        (*L)->data++;
    }
}
 
void CreateListEnd(linklist* L,int n)
{
    linklist p,end=*L;
    int i;
 
    for(i=0;i<n;i++)
    {
        p=(linklist)malloc(sizeof(Node));
        p->data=i;//rand()%100+1;
        p->Next=NULL;
        end->Next=p;
        end=p;
        (*L)->data++;
    }
}
 
Bool ListDeleteAll(linklist* L)
{
    linklist p=*L,q;
    while(p->Next)
    {
        q=p->Next;
        p->Next=q->Next;
        (*L)->data--;
        free(q);
    }
    free(p);
    return TRUE;
}
 
void Reverse(linklist* L)//翻转链表
{
    linklist cur, tmp;
 
    //初始化新的头结点
    cur=*L;
    cur=cur->Next;//旧链表的头结点被删除(赋给了新的头结点)
    (*L)->Next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
 
    while (cur)
    {
        tmp=cur;
        cur=cur->Next;//顺序删旧链表的元素
        tmp->Next=(*L)->Next;//被删掉的元素用头插法插入新的头结点
        (*L)->Next=tmp;
    }
}
 
int findMiddle(linklist L, ElemType* e)
{
    linklist fast,slow;
    int i;
    fast=slow=L;
 
    for(i=0;fast!=NULL&&fast->Next!=NULL;fast=fast->Next->Next)
    {
        slow=slow->Next;
        i++;
    }
    *e=slow->data;
    return i;
}

(3) 主程序main.c

#include <stdio.h>
#include "linklist.h"
 
int main()
{
    ElemType a;
    linklist list;
    int num;
 
    printf("           1.初始化\n");
    InitList(&list);
    traverseList(list);
 
    InitList(&list);
    printf("        2.尾插法新建单链表\n");
    CreateListEnd(&list,10);
    traverseList(list);
    ListDeleteAll(&list);
 
    InitList(&list);
    printf("        3.头插法新建单链表\n");
    CreateListHead(&list,10);
    traverseList(list);
 
    printf("            4.元素插入\n");
    ListInsert(&list,5,10);
    traverseList(list);
 
    printf("            5.元素删除\n");
    ListDelete(&list,5,&a);
    printf("被删除的元素为:%d  ",a);
    traverseList(list);
 
    printf("            6.元素查找\n");
    GetElem(list,3,&a);
    printf("第3个元素为:%d  \n\n\n",a);
 
    printf("             7.逆置链表\n");
    printf("原链表----------");
    traverseList(list);
    Reverse(&list);
    printf("逆置链表---------");
    traverseList(list);
 
    printf("\n\n             8.快慢指针查找中间结点\n");
    printf("原链表\n");
    traverseList(list);
    num=findMiddle(list,&a);
    printf("中间结点为%d,这是链表的第%d个结点\n\n\n",a,num);
 
    ListDeleteAll(&list);
    return 0;
}

###单链表编程实现----C++ (1)链表类的声明 linklist.h

#ifndef LIST_H
#define LIST_H
 
template<typename ElemType>//typedef int ElemType;
class List
{
public:
    struct Node
    {
        ElemType data;
        struct Node *next;
    };
    List();//链表,可以删除所有动态节点
    ~List();//删除全部节点
    void traverseList();//遍历
    bool ListInsert(int i,ElemType d);//在第i个节点位置插入元素d
    void CreateListHead(int n);//头插法插入
    void CreateListEnd(int n);//尾插法插入
    void Reverse();//逆置单链表
    bool ListDelete(int i,ElemType& e);//删除第i个元素
    bool GetElem(int i,ElemType& e);//获取第i个节点位置的元素,并且返回给e
    int findMiddle(ElemType& e);//查找中间结点,将查找的元素值赋给e,并返回该节点是第几个结点,如果是空表则返回0
private:
    Node *head;//存放头结点
};
 
#endif // LIST_H

(2)链表类的实现linklist.cpp

#include "linklist.h"
#include<iostream>
using namespace std;
 
template<typename ElemType>
List<ElemType>::List()//初始化
{
    head=new Node;
    head->next=NULL;
    head->data=0;//头指针的数据域也不要浪费:用来存放链表长度
 
}
 
template<typename ElemType>
List<ElemType>::~List()//删除
{
    Node* del;
    while (head->next!=NULL)
    {
        del=head;
        head=head->next;
        delete del;
    }
    delete head;
}
 
template<typename ElemType>
void List<ElemType>::traverseList()//遍历
{
    Node *ph=head;
    cout<<"链表总长度为:"<<head->data<<endl;
    ph=ph->next;
    for(;ph!=NULL;ph=ph->next)
        cout<<ph->data<<"  ";
    cout<<endl;
}
 
template<typename ElemType>
void List<ElemType>::CreateListHead(int n)//头指针插入
//头插法:
//head->NULL;
//head->p1->NULL;
//head->p2->p1->NULL;
{
    for(int i=0;i<n;i++)
    {
        Node* cur=new Node;
        cur->data=i;
        cur->next=head->next;
        head->next=cur;
        head->data++;
    }
}
 
template<typename ElemType>
bool List<ElemType>::ListInsert(int i,ElemType d)
{
    Node *p=head,*cur;
 
    for(int j=1;j<i;j++)
    {
        p=p->next;
        if(p==NULL)
            return false;
    }
    cur=new Node;
    cur->data=d;
    cur->next=p->next;
 
    p->next=cur;
 
    head->data++;
    return true;
}
 
template<typename ElemType>
void List<ElemType>::CreateListEnd(int n)//末尾插入
//尾插法:
//head->NULL;
//head->p1->NULL;
//head->p1->p2->NULL;
{
    Node* cur,*end;
    end=head;
    for(int i=0;i<n;i++)
    {
        cur=new Node;
        cur->data=i;
        cur->next=end->next;
        end->next=cur;
        end=cur;
        head->data++;
    }
}
 
template<typename ElemType>
bool List<ElemType>::ListDelete(int i,ElemType& e)
{
    Node *p=head,*cur;
 
    for(int j=1;j<i;j++)
    {
        p=p->next;
        if(p==NULL)
            return false;
    }
    cur=p->next;//待删除节点
    e=cur->data;
    p->next=cur->next;//将待删除节点的后一个节点赋给其前一个节点
    head->data--;
    delete cur;
    return true;
}
 
template<typename ElemType>
bool List<ElemType>::GetElem(int i,ElemType& e)
{
    Node* p=head;
 
    for(int j=0;j<i;j++)
    {
        p=p->next;
        if(p==NULL)
            return false;
    }
    e=p->data;
    return true;
}
 
/*
 *这个程序是原始版本的程序,这个程序的思路更加清晰,下面一个程序是改进的程序,代码更加简洁。
 */
/*
void List<ElemType>::Reverse()//翻转链表
{
    Node* cur=head;//头插法
    Node* newhead,*tmp;
    //初始化新的头结点
    newhead=head;
    cur=cur->next;//旧链表的头结点被删除(赋给了新的头结点)
    newhead->next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
    while (cur)
    {
        tmp=cur;
        cur=cur->next;//顺序删旧链表的元素
        tmp->next=newhead->next;//被删掉的元素用头插法插入新的头结点
        newhead->next=tmp;
    }
    head= newhead;//这一步是多余的,因为新头和旧头都是指向同一位置,其实newhead是一个多余变量,直接用head也是可以完成任务的。
}
*/
 
template<typename ElemType>
void List<ElemType>::Reverse()//翻转链表
{
    Node* cur,* tmp;//头插法
 
    //初始化新的头结点
    cur=head;
    cur=cur->next;//旧链表的头结点被删除(赋给了新的头结点)
    head->next=NULL;//注意这一句和上一句不能颠倒,不然cur无法向后移
 
    while (cur)
    {
        tmp=cur;
        cur=cur->next;//顺序删旧链表的元素
        tmp->next=head->next;//被删掉的元素用头插法插入新的头结点
        head->next=tmp;
    }
}
 
template<typename ElemType>
int List<ElemType>::findMiddle(ElemType& e)
{
    Node *fast,*slow;
    int i;
    fast=slow=head;
 
    for(i=0;fast!=NULL&&fast->next!=NULL;fast=fast->next->next)
    {
        slow=slow->next;
        i++;
    }
    e=slow->data;
    return i;
}

(3)主程序main.cpp

#include <iostream>
#include"linklist.cpp"
 
using namespace std;
 
int main()
{
 
    List<int> list;
    bool success;
    cout<<"                  1.初始化"<<endl;
    list.traverseList();
 
    List<int> list1;
    list1.CreateListEnd(10);
    cout<<endl<<endl<<"         2.尾插法新建单链表 "<<endl;
    list1.traverseList();
 
    List<int> list2;
    cout<<endl<<endl<<"         3.头插法新建单链表 "<<endl;
    list2.CreateListHead(10);
    list2.traverseList();
 
    cout<<endl<<endl<<"             4.元素插入"<<endl;
    list2.ListInsert(5,10);
    list2.traverseList();
 
    cout<<endl<<endl<<"             5.元素删除"<<endl;
    int a;
    list2.ListDelete(5,a);
    cout<<"被删除的元素为:"<<a<<"  ";
    list2.traverseList();
 
    cout<<endl<<endl<<"             6.元素查找"<<endl;
    list2.traverseList();
    success=list2.GetElem(3,a);
    if(!success)
        cout<<"error"<<endl;
    else
        cout<<"被查找的元素为:"<<a<<endl;
 
    cout<<endl<<endl<<"             7.逆置链表"<<endl;
    cout<<"原链表"<<endl;
    list2.traverseList();
    list2.Reverse();
    cout<<endl<<"逆置链表"<<endl;
    list2.traverseList();
 
    cout<<endl<<endl<<"             8.快慢指针查找中间结点"<<endl;
    int num;
    cout<<"原链表"<<endl;
    list2.traverseList();
    num=list2.findMiddle(a);
    cout<<"中间结点为"<<a<<",这是链表的第"<<num<<"个结点"<<endl<<endl<<endl;
    return 0;
}

###运行结果

运行结果.png