数据结构之链表(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.总结
总结:学数据结构,一种比较好的学习方式是画图,(由于笔者刚写博客,这些操作不熟悉,以后补上)图形很直观,能够让大家理清思路,希望大家多尝试尝试。链表的出错点和难点不仅仅在于核心步骤,还在于边界条件的考虑,防止溢出,我们要考虑周全,才能写出具有健壮性的程序。
- 数据结构之链表(C/C++)
- 数据结构之链表(c)
- 数据结构之链表编程(C++)
- C - 数据结构之链表
- c语言数据结构之链表
- 数据结构之c语言链表
- C语言数据结构之双向链表
- 数据结构之链表(C实现)
- C 语言 数据结构之双向链表
- C - 数据结构之 循环链表
- 数据结构之链表——c
- 数据结构之链表(C实现)
- c语言数据结构之通用链表
- 数据结构(c语言版)之顺序表
- 数据结构之线性表(C语言版)
- 数据结构之单循环链表C++(模板)
- 数据结构学习笔记之链表(C语言版)
- 数据结构之双向链表(C语言实现)
- Android 在桌面显示对话框
- 线性表的顺序存储和链式存储
- HDU5492-Find a path
- Cortex-M的M0,M+,M3,M4,M7几种内核的简单区别
- mac上忘记mysql密码完美解决方案
- 数据结构之链表(C/C++)
- Linux中more和less命令用法
- 前端面试题:从url到页面展现,这之中发生了什么?
- 剑指offer-47.求1+2+3+...+n
- c++运算符重载
- Netty Failure to transfer io.netty:netty-tcnative:jar:${os.detected.classifier}:2.0.0.Final 问题解决
- 湖北民族学院oj 1883 之 Sequence Number
- as中drawable和mipmap都用右键res,new image asset方法放入
- Angular4中的依赖注入