单向链表

链表

一、线性表

线性表是最基本,最简单,也是最常用的一种数据结构

大部分情况下,表中元素除了表头和表尾,其余元素只有一个直接前驱,只有一个直接后继

二、线性表存储结构

1、顺序结构---链式结构

顺序结构:数组

链式结构:链表

三、数组

数组是有序的元素序列,用于储存多个相同类型数据的集合

1、数组的基本特征

逻辑位置连续,物理位置连续

物理位置:在内存中地址是连续的

2、数组的插入

要保证有足够的空间,如果没有,会把某个空间的原来的值覆盖掉

3、数组的删除

不是把物理上的内存地址给删除掉,而是用数组元素把要删除的元素从后往前覆盖掉

在插入和删除是不能破坏顺序表结构,在插入时要留出待插入位置,在删除时要覆盖掉被删除的位置的数据

#include<iostream>
using namespace std;

#define MAX_LEN 10
int main()
{
	int arr[MAX_LEN] = { 1,2,3,4,5,6,7,8,9,10 };
	//数组的插入
	//要保证有足够的空间,如果没有,会把某个空间的原来的值覆盖掉
	int len = 10;
	int insertval = 123;//待插入的数
	int insert_index = 3;//插入位置的下标
	//把待插入位置后面的位置依次往后移位,腾出空间来进行插入
	for (int i = len-1; i > insert_index; i--)//在数组下标为3插入元素
	{
		arr[i] = arr[i - 1];
	}
	arr[insert_index] = insertval;//插入数据

	//数组元素的删除
    //实际上在内存中这个元素还是存在
	int delete_index = 3;//删除位置的下标
	for (int i = delete_index; i < len-1; i++)//删除数组下标为3的元素
	{
		arr[i] = arr[i + 1];//但是会存在一个问题,就是arr[9],这个位置还是原来的数,所以arr[8]==arr[9]了
	}
	len--;
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}

	getchar();
	getchar();
	return 0;
}


四、链表

1、链表的概念

物理顺序不一定连续(所以需要有一个数据专门来保存物理位置)

逻辑位置连续

链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

链表由一系列节点(链表中每一个元素称为节点)组成,结点可以在运行时动态生成

链表里至少要有两个数据,一个是保存物理地址,一个是逻辑数据(没有这个的话就没有意思了),所以必须要用结构体来存储数据

链表是你自己写的数据结构

2、链表的组成--两个部分

数据域:存储数据元素

指针域:存储下一个结点的地址

3、链表的生成

#include<iostream>
using namespace std;

struct mynode
{
	int id;//表示数据域(可以有多个数据)
	//指针域(可以有多个指针)
	mynode* pnext;//8个字节
};
int main()
{
	/*
	mynode m;//这是一个结点,栈区
	m.id = 1;
	m.pnext = new mynode;// 新申请一个节点,堆区
	m.pnext->id = 2;
	m.pnext->pnext = new mynode;//一般不用这种方式,栈区和堆区不要混合着用
	*/

	mynode* phead = NULL;//链表头指针
	mynode* pcurrent = NULL;//头指针不能轻易修改,防止链表丢失
	phead = new mynode;//申请一个节点
	phead->id = 1;//数据元素
	phead->pnext = NULL;//将头指针里存储上第一个节点的首地址

	phead->pnext = new mynode;//第一个节点的指针指向第二个节点的首地址
	phead->pnext->id = 2;//这个pnext是第一个节点的pnext
	phead->pnext->pnext = NULL;//第二个pnext是第二个节点的pnext
	phead->pnext->pnext = new mynode;//第二个pnext申请了一个节点,里面两部分组成,一个是数据,一个是指针
	phead->pnext->pnext->id = 3;
	phead->pnext->pnext->pnext = NULL;


    mynode* phead = NULL;//链表头指针
	mynode* pcurrent = NULL;//头指针不能轻易修改,防止链表丢失
    //尾部添加2
	for (int i = 0; i < 10; i++)
	{
		if (phead == NULL)
		{
			phead = new mynode;
			phead->id = i + 1;
			phead->pnext = NULL;
			pcurrent = phead;//当前链表的最后一个节点,就是让pucrrent指向phead指针
		}
		else
		{
			pcurrent->pnext = new mynode;
			pcurrent->pnext->id = i + 1;
			pcurrent->pnext->pnext = NULL;
			pcurrent = pcurrent->pnext;//这个pcurrent是指向当前链表的最后一个节点
		}
	}

    
    //头部添加 
	for (int i = 0; i < 10; i++)
	{
		pcurrent = new mynode; 
		pcurrent->id = (i + 1) * 10;//赋值操作
		pcurrent->pnext = phead;//就是不断让当前指针指向头指针所指的地址
		phead = pcurrent;//让头指针指向当前链表节点,右一个先后顺序

	}
    
    
    
    
    //插入数据
	int insertval = 2;//待插入数据
	mynode* pinsertnode = new mynode;//创建一个新的节点
	pinsertnode->id = insertval;//存储待插入数据
	pinsertnode->pnext = NULL;//置空这个节点的指针域
    //外部数据单独管理,内部数据单独管理
    
    
	if (phead)
	{
		//链表存在(按升序来比较)
		if (phead->id > insertval)//如果头指针指向的节点里的数据比待插入数据大,让phead头指针指向将创建的那个新的节点
		{
			pinsertnode->pnext = phead;//让两个节点链接起来,在新创建的节点的指针域里面存储头指针之前指向的那个节点的首地址
			phead = pinsertnode;//让头指针指向新创建的节点
		}
		else//如果如果头指针指向的节点里的数据比待插入数据小,将这个新的节点往后面移动
		{
			pcurrent = phead;//接收头指针
			while (pcurrent->pnext)//因为头指针里面的数据已经比过了,所以直接指向下一个节点
			{
				if (pcurrent->pnext->id > insertval)//如果下一个节点里面的数据大于待插入数据,直接退出循环,就插在那两个节点之间
					break;
				pcurrent = pcurrent->pnext;//如果下一个节点里面的数据比待插入数据小,那么就还需要将这个pcurrent指针向后面移动
			}//这个循环之后,就找到可以插入那个节点的位置了
			pinsertnode->pnext = pcurrent->pnext;//让新创建的节点的指针域指向那个待插入位置节点的指针域,就是将这个链表的后面连起来了,现在通过pinsertnode可以访问到pcurrent->pnext之后的所有数据
			pcurrent->pnext = pinsertnode;//将新创建的节点的首地址赋值给pcurrent->pnext,通过pcurrent->pnext的pnext里面所存储的首地址可以访问到链表的后面部分
		}
	}
	else//链表不存在
		phead = pinsertnode;//指直接让头指针指向当前指针
    
    	//链表的遍历(表中结点都是通过上一个节点的指针域来访问)
	pcurrent = phead;//链表的头指针不能轻易修改,因为对链表的所有操作基本上都是通过都指针来完成的
	while (pcurrent)//刚开始pcurrent指针是指向链表的头指针的,里面保存了链表的第一个节点里的数据
	{
		printf("%d\n", pcurrent->id);
		pcurrent = pcurrent->pnext;//从前往后指针改变
	}
    
    
    //l链表的删除
	int deleteval = 1;
	if (phead)//如果头指针不为空执行
	{
		if (phead->id == deleteval)//链表头的数据是要删除的数据
		{
			pcurrent = phead;//接受头指针
			phead = phead->pnext;
			delete pcurrent;
		}
		else
		{
			pcurrent = phead;
			while (pcurrent->pnext)
			{
				if (pcurrent->pnext->id == deleteval)
				{
					mynode* temp = pcurrent->pnext;//临时指针接收要被删除的节点
					pcurrent->pnext = temp->pnext;
					delete temp;
					break;
				}
				pcurrent = pcurrent->pnext;
			}
		}
	}
    
    
    //链表的清除
	while (phead)
	{
		pcurrent = phead;
		phead = phead->pnext;
		delete pcurrent;
	}
	pcurrent = NULL;

	getchar();
	getchar();
	return 0;
}


五、数组和链表的区别

数组优点:查找快,尾部数据的插入删除方便

数组缺点:中间或前端的插入和删除麻烦,一次需要一个连续内存,在操作系统会受限

链表优点:插入和删除很方便,只是指针的改变,可以利用内存碎片

链表缺点:查找麻烦

posted @ 2021-03-09 09:18  kisfly  阅读(53)  评论(0编辑  收藏  举报