链表的多种实现方式

 链表是什么???

  链表是一种区别于顺序储存的非连续、非顺序的物理储存结构,也就是说,不像队列、堆栈之类的逻辑数据结构,它只是一种数据储存的物理结构。我们所使用的数组便是一种物理储存结构。

  但与数组不同的是,它是链式储存的,而数组采用了顺序储存。

  那么,什么是顺序储存与链式储存呢(⊙o⊙)?

  顺序储存:把逻辑上相邻的结点存储在物理位置上相邻的存储单元,即每个结点之间的地址是连续的。

  链式储存:用一组任意的储存单元存储线性表的数组元素,结点之间的地址不一定是连续的,但每个结点之间一定有着某种先后关系。

  我们使用链式储存,可以更方便地插入、删除、更新元素,但是频繁查询元素时较耗时间。同时,链表由于多了指针域,更耗空间,却又避免了数组的大幅空间浪费的情况。

链表代码之指针实现:

//链表(包括有关链表的各种操作) 
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef struct List *Link;//Link代表链表指针 
typedef struct List Lnode;//Lnode代表链表结点 

struct List
{
    int data;
    Link next;
};


Link create(Link Head)//建表操作并返回头指针 
{
    int newdata;
    Link newnode;
    Head=(Link)malloc(sizeof(Lnode));
    printf("Please input number:\n");
    scanf("%d",&newdata);
    Head->data=newdata;
    Head->next=NULL;
    while(1)
    {
        newnode=(Link)malloc(sizeof(Lnode));
        if(newnode==NULL)//若空间分配失败 ,则终止循环 
        {
            printf("Sorry, it failed!"); 
            break;
        }
        printf("Please input number : intput '-1' means exit\n");//输入链表元素,输入-1代表输入结束 
        scanf("%d",&newdata);
        if(newdata==-1) return Head;
        newnode->data=newdata;
        newnode->next=Head;
        Head=newnode;
    }
    return Head;
}

void dispaly(Link Head)//链表元素的显示 
{
    Link p=Head;
    if(p==NULL)//链表为空 
        printf("\nList is empty\n");
    else
        do
        {
            printf("%d",p->data);
            p=p->next;
        }while(p!=NULL);
    return;
}

Link insert(Link Head, int value, int i)//Head为头指针,value为插入值,i为插入结点的编号 
{
    Link newnode,p=Head;
    int newdata,j=1;
    newnode=(Link)malloc(sizeof(Lnode));
    newnode->data=value;
    if(i==1)
    {
        newnode->next=Head; Head=newnode;
    } else {
        while(j<i-1&&p->next!=NULL)//找到 i 结点的前一个元素 
        {
            p=p->next;
            j++;
        }
        if(j==i-1)
        {
            newnode->next=p->next;
            p->next=newnode;
        }
        else printf("Insert is Failure,i is not right!\n");
    }
    return Head;
}

Link del(Link Head, int i)//Head为头指针,i为删除结点编号
{
    Link p=Head,t;
    int j=1;
    if(i==1)
    {
        Head=Head->next;
    } else {
        while(j<i-1&&p->next!=NULL)
        {
            p=p->next;
            j++;
        }
        if(j==i-1&&p->next!=NULL)
        {
            t=p->next; p->next=t->next;
        }
        if(t!=NULL) free(t);//释放内存,尽可能地节省空间 
    }
    return Head;
}

int get(Link Head, int i)//取编号为i的结点的值 
{
    int j=1;
    Link p; p=Head;
    while(j<i&&p!=NULL)
    {
        p=p->next; j++;
    }
    if(p!=NULL) return p->data;
    else printf("Data is error!\n");
    return -1;
}

int locate(Link Head, int x)//查找第一个值为x的结点的编号,若失败,则返回-1 
{
    int n=1;
    Link p=Head;
    while(p!=NULL&&p->data!=x)
    {
        p=p->next;
        n++;
    }
    if(p!=NULL) return n;
    else printf("Not found!\n");
    return -1;
}

int lengh(Link Head)//取链表长度 
{
    int len=0;
    Link p=Head;
    while(p!=NULL)
    {
        len++;
        p=p->next;
    }
    return len;
}

Link connect(Link Head1, Link Head2)//以前面的链表为头,连接后续链表 
{
    Link p=Head1;
    while(p->next!=NULL)
    {
        p=p->next;
    }
    p->next=Head2;
    return Head1;
}

bool compare(Link Head1, Link Head2)//比较两个链表是否相同 
{
    Link p1,p2;
    p1=Head1;
    p2=Head2;
    while(1)
    {
        if(p1->next==NULL&&p2->next==NULL)
        return 1;
        else if(p1->data!=p2->data)
        return 0;
        else {
            p1=p1->next;
            p2=p2->next;
        }
    }
}


Link Set_NULL(Link Head)//清空链表 
{
    Link p; p=Head;
    while(p!=NULL)
    {
        p=p->next;
        free(Head);
        Head=p;
    }
    return Head;
}

int main()
{
    //链表的相关操作已给出
    //以下省略一千字...... 
} 
View Code

  但实际上,我们同样可以用数组来模拟链表,而且对其操作的速度将大大提高(我也是在一位神犇那儿知道的),因为CPU缓存对连续的数据结构速度更快(貌似是因为储存结构层次之类乱七八糟的东西)!

  因此,下面给出数组模拟链表的代码。

链表代码之数组实现:

//数组仿真链表的演示程序
#include<iostream>
#include<cstdlib>
#define MAXN 100001//链表中最多能容纳的元素数+1
using namespace std;

int linklst_data[MAXN] ;//linklst_data 为链表中元素的数据
int linklst_point[MAXN] ;//linklst_point 为链表中元素的指针
int head = -1;/*链表的头指针*/

void del_by_data(int del_data)//删除一个含有del_data的元素
{
  int p=head,pre=-1;//从头开始遍历链表
  while(p != -1)
  {
    if(linklst_data[p] == del_data)//如找到了要删除的值,则进行删除操作
    {
      if(p == head)//如果删除的是头指针,则更新头指针
        head = linklst_point[head];
      else//这个else 等同于 if (pre != 1)因为当p!=head时p前面已经有元素了
        linklst_point[pre] = linklst_point[p];
      linklst_data[p] = -1;//删除p指向的元素的值*/
      linklst_point[p] = -1;
      return ;/*结束删除操作*/
    }
      pre = p;
      p = linklst_point[p];
  }
}

void add_front(int add_data)//在链表前段加入元素
{
  int p = 1;
  while(linklst_data[p] != -1 && p < MAXN)  //找一空位存储数据,这里可以优化
    ++p;
  linklst_data[p] = add_data; //将要加入的节点选好的空位赋值
  linklst_point[p] = head;  //将当前加入元素的指针指向head
  head = p; //使当前的元素成为链表头指针
}

void add_rear(int add_data)//在链表末尾加入元素
{
  int p = 1,pre;
  while(linklst_data[p] != -1 && p < MAXN )//找到空位 
    ++p;
  linklst_data[p] = add_data; //该空位赋值
  if( head != -1 ) //链表不为空
  {
    pre = head;//找到链表中的最后一个元素*/
    while(linklst_point[pre] != -1)//找到空指针 
      pre = linklst_point[pre];
    linklst_point[pre] = p;//将当前链表中最后一个元素的指针指向要加入的元素
  }
  else//否则直接对head所指的元素赋值
    head = p;
  return ;
}

void output()
{
  int p=head;
  cout<<"list is: ";
  while(p != -1)
  {
    cout<<linklst_data[p]<<" ";
    p = linklst_point[p];
  }
  cout<<"\n";
}

void init()//初始化数组指针值
{
  for(int i = 0;i < MAXN;i++)//链表中的空值设定为-1
  {
    linklst_point[i] = -1;
    linklst_data[i] = -1;
  }
}

int main()//演示链表的操作
{
  int ins,data;
  init();
  while(1)
  { 
    cout<<"1.insert a value in front \n";
    cout<<"2.insert a value in rear \n";
    cout<<"3.delete a value \n";
    cout<<"4.quit \n";
    cin>>ins;
    switch(ins)
    {
        case 1:
        cout<<"please insert a value :";
        cin>>data;
        add_front(data);
        break;
        case 2:
        cout<<"please insert a value :";
        cin>>data;
        add_rear(data);
        break;
      case 3:
        cout<<"please insert a value :";
        cin>>data;
        del_by_data(data);
        break;
      default:    
        return 0;
    }
    system("cls");//清空屏幕,此命令只能在window平台下使用 
    output();
 }
 return 0;
}
View Code

  从代码中可以发现,每次进行插入操作时,都要在数组中找一空位进行操作,这就导致每次插入操作的时间复杂度都是线性的,导致很大的时间浪费。

  因此,我们可以通过堆栈记录没有被加入链表的元素的下标,每次插入操作只需从栈中获取下标即可。这样,O(n)的线性查找便转化为了O(1)的出栈操作,时间上大大优化。

链表代码之数组实现(堆栈优化):

//数组链表的演示程序(栈优化)
#include<iostream>
#include<cstdlib>
#define MAXN 100001
using namespace std;

int linklst_data[MAXN] ;
int linklst_point[MAXN] ;
int stack[MAXN];//用于记录没有加入链表的数组下标的栈
int head = -1;//链表的头指针
int stack_head = 0;//栈顶指针 

void del_by_data(int del_data)//删除一个含有del_data的元素
{
  int p=head,pre=-1;  //从头开始遍历链表
  while(p != -1)
  {
    if(linklst_data[p] == del_data)//找到要删除的值,进行删除操作
    {
      
      if(p == head)//如果删除的是头指针,则更新头指针
        head = linklst_point[head];
      else//这个else 等同于 if (pre != 1)因为当p!=head时p前面已经有元素了
        linklst_point[pre] = linklst_point[p];
      
      linklst_data[p] = -1;//删除p指向的元素的值
      linklst_point[p] = -1;
      stack[--stack_head] = p;//将删除元素的数组下标入栈
      return ;
    }
      pre = p;
      p = linklst_point[p];
  }
  return ;
}

void add_front(int add_data)//在链表前段加入元素*/
{
  int p = 1;
  p = stack[stack_head++];//直接取出栈中的空位*/
  linklst_data[p] = add_data;//将要加入的节点选好的空位赋值*/
  linklst_point[p] = head; //将当前加入元素的指针指向head*/
  head = p;  //使当前的元素成为链表头指针*/
}

void add_rear(int add_data)//在链表末尾加入元素*/
{
  int p = 1,pre;
  p = stack[stack_head++];//直接取出栈中的空位*/
  linklst_data[p] = add_data;  //对要加入的节点的空位进行赋值*/
  if( head != -1 ) //链表不为空
  {
    pre = head;//找到链表中的最后一个元素*/
    while(linklst_point[pre] != -1)
        pre = linklst_point[pre];
    linklst_point[pre] = p;//将当前链表中最后一个元素的指针指向要加入的元素
  }
  else//否则直接对head所指的元素赋值
    head = p;
}

void output()
{
  int p=head;
  cout<<"List: ";
  while(p != -1)
  {
    cout<<linklst_data[p]<<" ";
    p = linklst_point[p];
  }
  cout<<"\n";
  return ;
}


void init()//初始化数组指针值
{
  for(int i = 0;i < MAXN;i++)
  {
    linklst_point[i] = -1;
    linklst_data[i] = -1;
    stack[i] = i + 1;//从1到MAXN给栈加入数组下标
  }
  return ;
}

int main()//演示链表的操作
{
  int ins,data;
  init();
  while(1)
  { 
    cout<<"1.insert a value in front \n";
    cout<<"2.insert a value in rear \n";
    cout<<"3.delete a value \n";
    cout<<"4.quit \n";
    cin>>ins;
    switch(ins)
    {
        case 1:
        cout<<"please insert a value :";
        cin>>data;
        add_front(data);
        break;
        case 2:
        cout<<"please insert a value :";
        cin>>data;
        add_rear(data);
        break;
      case 3:
        cout<<"please insert a value :";
        cin>>data;
        del_by_data(data);
        break;
      default:
        return 0;
    }
    system("cls");//此命令只能在window平台下使用 
    output();
 }
 return 0;
}
View Code

  以上便是我所学习的第一个数据结构--链表了,若有不足,请指正!!!

O(∩_∩)O谢谢

猜你喜欢

转载自www.cnblogs.com/Beauty-of-wisdom/p/10084023.html