数据结构之链表

数据结构之链表见解

链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻 .(也可以说链表是一种物理存储结构上非连续,非顺序的存储结构)

说的通俗的就像你出去串门,需要去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; 
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值