数据结构之链表见解
链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻 .(也可以说链表是一种物理存储结构上非连续,非顺序的存储结构)
说的通俗的就像你出去串门,需要去A,B,C三个朋友家,彼此间都有距离可以理解为都不在一个村,但我访问只需要先到A的家,在去B的家,最后去C的家就行了.按顺序去访问三个人就像了,无非就是多跑点路,逻辑上有访问顺序就行了,距离上不必有联系.
链表里面逻辑上的相邻关系的表示:通过给每个元素附加一个指针域,指向下一个元素的存储位置。如下图所示:
在上图中可以明显的看出: 每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址, 因此指针指向的类型也是结点类型 .因为它存在的意义就是为了能访问到下一个节点的地址.
理解例题代码:
......
struct STAR{ //自定义的结构体
.....
};
struct Link_Node{
struct STAR star; //自己定义的数据,类型可以自己根本要编写的代码来指定
struct Link_Node *next;
//上面是定义的指针域,next前面的类型跟定义的结构体相同是为了能指向下一个结点的地址
}
链表的核心要素:
1) 每个节点由数据域和指针域组成
2)指针域指向下一个节点的内存地址
其结构体定义:
Typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
}LinkList, LinkNode;
LinkList, LinkNode都可以理解为struct LinkNode的别名
习惯上一般会把**LinkList表示为首节点(头结点)**(不存储数据的)
**LingNode表示为链表的节点(首节点后面的节点)**
单链表
一.单链表的概念:
链表的节点均单向指向下一个节点,形成一条单向访问的数据链
上图说明:
1)头节点里面是不存储数据的
2)每个节点后面的数字代表指针域里面的地址,即里面保存的是下一个节点的地址,可以访问到下一个地址.最后一个节点没有下一个节点,那把它指向下一个节点的指针域置为0,即NULL.表示节点就结束了
3)访问单链表里面的数据,只能从头节点开始找到它的下一个节点的地址,通过地址访问到下一个节点.后面才能一个一个访问后面的节点.单链表不能像数组一样,只要知道下标,知道数组的首地址,就可以通过偏移,来取得元素的值.单链表的存储表示一个挨一个的,必须要通过上一个节点才能访问到下一个节点,中间只要有故障就不能继续访问了
二.单链表的初始化
初始化代码:
typedef struct _LinkNode {
int data; //结点的数据域
struct _LinkNode *next; //结点的指针域
}LinkNode, LinkList; //链表节点、链表头节点
bool InitList(LinkList* &L)//构造一个空的单链表 L
{
L=new LinkNode; //生成新结点作为头结点,用头指针 L 指向头结点
if(!L)return false; //生成结点失败 //合法性检查
L->next=NULL; //头结点的指针域置空
return true;
}
如下图所示,初始化的节点
三. 单链表增加元素
(1)前插法
bool ListInsert_front(LinkList* &L, LinkNode * node){
if(!L || !node ) return false; //合法性判断
node->next = L->next;
L->next = node;
return true;
}
节点说明:
1)在节点没有插入之前,头节点的next指向的是节点1的地址.当新节点(节点2)插入之后,则需要把新节点的next指向原来头节点的next,即节点1的地址.后面再把头节点的next指向新节点就行了.
完整代码:
#include<iostream>
using namespace std;
typedef struct _LinkNode {
int data; //结点的数据域
struct _LinkNode *next; //结点的指针域
}LinkNode, LinkList; //链表节点、链表头节点
bool InitList(LinkList* &L)//构造一个空的单链表 L
{
L = new LinkNode; //生成新结点作为头结点,用头指针 L 指向头结点 //分配一个动态内存
if (!L)return false; //生成结点失败 //合法性检查
L->next = NULL; //头结点的指针域置空
return true;
}
//前插法
bool ListInsert_front(LinkList* &L, LinkNode * node) {
if (!L || !node) return false; //合法性判断
node->next = L->next;
L->next = node;
return true;
}
//输出
void ListPrint(LinkList* &L) {
LinkNode *p = NULL;
if (!L) {
cout << "链表为空" << endl;
return;
}
p = L->next;
while (p) {
cout << p->data << "\t";
p = p->next;
}
cout << endl;
}
int main(void) {
LinkList *L = NULL;
LinkNode *s = NULL;
//1.初始化一个空的链表
InitList(L);
//2.使用前插法插入数据
int n;
cout << "前插法创建单链表" << endl;
cout << "请输入元素个数n:";
cin >> n;
cout << "\n请依次输入n个元素:" << endl;
while (n > 0) {
s = new LinkNode; //生成新节点s
cin >> s->data;
ListInsert_front(L,s);
n--;
}
//3.单链表的输出
ListPrint(L);
system("pause");
return 0;
}
代码运行结果如下:
(2)尾插法
//尾插法
bool ListInsert_back(LinkList* &L, LinkNode *node){
LinkNode *last = NULL;
if(!L || !node ) return false;
//找到最后一个节点
last = L;
while(last->next) last=last->next;
//新的节点链接到最尾部
node->next = NULL;
last->next = node;
return true;
}
代码说明:
1)定义一个last指针,用它从头节点开始寻找(last = L),直到找到 last->next的值为空,即last此时为最后一个节点了.这样最后一个节点的位置就能方便找到了
运行代码如下:
(3)单链表任意位置输入
//任意位置插法
bool LinkInsert(LinkList* &L, int i, int &e)//单链表的插入
{
//在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点
int j;
LinkList *p, *s;
p=L;
j=0;
while (p&&j<i-1) //查找第 i-1 个结点,p 指向该结点
//j<i-1:找到需要插入点的前一个节点,那么需要跳过的节点数.即需要定位到插入点的前一个位置
{
p=p->next;
j++;
}
if (!p || j>i-1){//i>n+1 或者 i<1
return false;
}
s=new LinkNode; //生成新结点
s->data=e; //将新结点的数据域置为e
s->next=p->next; //将新结点的指针域指向结点 ai
p->next=s; //将结点 p 的指针域指向结点 s
return true;
}