数据结构之链表(C/C++)

来源:互联网 发布:jdk 7u79 windows x86 编辑:程序博客网 时间:2024/05/11 01:05

引言

从本片文章开始,我准备向大家介绍一些基本的数据结构与算法。

1.什么是链表

链表是线性表的链式存储结构,它用一组物理上不一定相邻的存储单元来存储线性表中的数据元素。
在单链表中,每一个数据元素由一个点表示,该节点包含两部分信息:数据自身的信息和该元素后继的存储地址。数据域存放元素信息,指针域存放后继地址。

结点定义如下:-

struct Node{    int data;       //数据域    Node *next;     //指针域};

本书的代码都是基于开发工具vs2013

链表是最最基本的一种数据结构,它和数组一样,都是我们构成一些复杂数据结构的前提。

2.链表的创建及其基本操作

(1)链表的创建

链表的创建就是创建一个一个结点的过程,我们首先创建一个带头结点的空单链表,再将结点一个一个插入。共有两种插入方法,头插法和尾插法。代码如下:

//头插法建立链表void HeadCreate(Node *head, int a[],int n){    for (int i = 0; i < n; i++)    {        Node *p = new Node;             //每次循环新建一个结点p        p->data = a[i];                 //为新结点p赋值        p->next = head->next;           //p的下一个结点等价于头结点的下一个结点        head->next = p;                 //头结点的下一个结点为p 等价于每隔新建的结点都插入到头结点的后面    }}//尾插法建立链表void TailCreate(Node *head, int a[], int n){    Node *rear = head;                  //指向表尾元素,最初只有头结点,所以指向头结点    for (int i = 0; i < n; i++)             {        Node *s = new Node;             //同上面一样,创建新的结点s        s->data = a[i];                 //赋值        rear->next = s;                 //新建结点s放到尾结点的后面        rear = s;                       //尾结点等于新建的结点,等价于尾结点的后移,因为尾结点总是指向最后一个结点    }    rear->next = NULL;                  //很关键一步,最后一个节点的指针域为空}

我们预先设立一个头结点head,它的数据域对我们来说没有意义(当然也可以存放一些数据如表长,我们这里不做讨论),它的存在能简化操作。

(2)链表的打印

既然创建好了链表,我们先尝试着打印出来看看情况。

//打印链表void Print(Node *head){    Node *p = head->next;               //头结点不必打印    while (p)    {        cout << p->data << endl;        //输出结果        p = p->next;                    //结点后移    }}

来测试下吧!

#include<iostream>using namespace std;struct Node{    int data;       //数据域    Node *next;     //指针域};//头插法建立链表void HeadCreate(Node *head, int a[],int n){    for (int i = 0; i < n; i++)    {        Node *p = new Node;             //每次循环新建一个结点p        p->data = a[i];                 //为新结点p赋值        p->next = head->next;           //p的下一个结点等价于头结点的下一个结点        head->next = p;                 //头结点的下一个结点为p 等价于每隔新建的结点都插入到头结点的后面    }}//尾插法建立链表void TailCreate(Node *head, int a[], int n){    Node *rear = head;                  //指向表尾元素,最初只有头结点,所以指向头结点    for (int i = 0; i < n; i++)             {        Node *s = new Node;             //同上面一样,创建新的结点s        s->data = a[i];                 //赋值        rear->next = s;                 //新建结点s放到尾结点的后面        rear = s;                       //尾结点等于新建的结点,等价于尾结点的后移,因为尾结点总是指向最后一个结点    }    rear->next = NULL;                  //很关键一步,最后一个节点的指针域为空}void Print(Node *head){    Node *p = head->next;               //头结点不必打印    while (p)    {        cout << p->data << endl;        //输出结果        p = p->next;                    //结点后移    }}int _tmain(int argc, _TCHAR* argv[]){    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };    int n = 10;    Node *head = new Node;              //建立一个结点    head->next = NULL;                  //指针域为空,即空结点    HeadCreate(head, a, n);                 Print(head);                            cout << endl;                           TailCreate(head, a, n);                 Print(head);                            return 0;}

如图所示

由此可见头插和尾插的区别。头插是把结点一个一个插入到头结点后面,所以是倒序的 尾插是把结点一个一个插入到链表尾部,是正序的。

(3)链表的查找

查找有两种:

  • 按位查找
  • 按值查找

按位查找是根据根据结点的序号进行查找。按值查找是根据结点的元素进行查找。
代码如下:

//单链表的按位查找,返回值是结点元素  int Search(Node *head,int i,int n)                    {      Node *p = head->next;                //p是头结点下一个结点,也是首个含有元素的结点    int j = 1;                           //j用来计数    if (i <= 0 || i > n)                 //好的程序应该具有健壮性,能及时检测出错误输入    {          cerr << "该结点不存在"<<endl;          exit(1);      }      while (p&&j<i)                       //j通过记数,找到该删除的那个结点    {          p = p->next;          j++;      }        return p->data;                      //返回查找值}  //单链表的按值查找,返回值是结点序号  int Locate(Node *head,int data)  {      Node *p = head->next;      int j = 1;                       //用来记数    while (p && p->data != data)     //当结点p不为空并且没有和指定数据相匹配时      {          p = p->next;                 //向下寻找               j++;                         //数字增加    }      if (p)                           //p不为空,说明找到了那个结点        return j;      else          return  0;  }  

(4)链表的插入

链表的插入和链表的创建相似,有前插法和后插法,因为链表的创立就是通过一个一个结点不停地创建而形成的。

后插法将待插入结点插入到指定结点的后面。

设 p为指定结点,s为待插入结点,步骤如下:

s->next = p->next;  p->next = s;  

这里顺序是不能颠倒的

前插法首先需要找到指定结点的前一个结点,再把待插入结点插入到前一个节点的后面。

设 p为指定结点,前继结点为p ,s为待插入结点,步骤如下:

q = head;  while (q->next != p)             //找到p的前一个结点      q = q->next;  s->next = q->next;  q->next = s;  

由此可以看出,后插法的效率比前插法要高,下面给出后插法的具体实现。

void Insert(Node *head,int i,int data)  {      Node *p = head;      int j = 1;      while (p && j<i)      {          p = p->next;          j++;      }      if (!p)      {          cerr << "插入位置非法" << endl;          exit(1);      }      else      {          Node  *s = new Node;        //生成元素值为item的新结点s          s->data = data;             //赋值          s->next = p->next;                  p->next = s;                    }  }  

(5)链表的删除

若要删除结点q,首先要找到q的前继结点p,然后将p的后继结点指向q的后继结点。步骤如下:

p->next = q->next;  delete q;  

代码如下:

void Delete(Node *head, int i)  {      Node *p = head;      int j = 1;      while (p && j < i)                  //这里找到的p结点是删除结点的前一个结点    {          p = p->next;          j++;      }      if (!p || !p->next)    {          cerr << "删除位置非法";          exit(1);      }      else      {          Node *q = p->next;          p->next = q->next;          delete q;      }  }  

测试代码:

int _tmain(int argc, _TCHAR* argv[])  {      int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };      int n = 10;      Node *head = new Node;              //建立一个结点      head->next = NULL;                   //指针域为空,即空结点      TailCreate(head, a, n);                   cout << "第4个结点的数据是:"<<Search(head, 4, n)<<endl;      cout << "数据为7的结点是第:" << Locate(head, 7)<<"个"<<endl;      Insert(head, 11, 13);               //在第11个位置插入13,即最后一个位置插入      Print(head);                              cout << endl;                           Delete(head, 5);                    //删除第5个结点      Print(head);      return 0;  }  

结果如下:
这里写图片描述


3.总结

总结:学数据结构,一种比较好的学习方式是画图,(由于笔者刚写博客,这些操作不熟悉,以后补上)图形很直观,能够让大家理清思路,希望大家多尝试尝试。链表的出错点和难点不仅仅在于核心步骤,还在于边界条件的考虑,防止溢出,我们要考虑周全,才能写出具有健壮性的程序。

0 0
原创粉丝点击