数据结构与算法1-2 线性表之单链表

1.单链表的概念

(1)结点

  在链表结构中,构成链表的基本单位就是结点,每个结点结构体一般包括数据域指针域。在数据域中,包含用户想要存储的每个单位的信息;指针域则是存储下一结点的指针,由此才能将若干结点连接成为链表。结点的结构一般如下所示:
在这里插入图片描述
         图1. 结点的结构

(2)单链表

  单链表,顾名思义,就是将若各干存储信息的结点,通过每个结点指针域的指针依次连接起来。与顺序表不同的是,单链表在内存中并不是连续分布的,而是随机分布,这也就决定了单链表的一个重要特性——在使用前无需一次性分配所有结点所需的空间,而是根据需要随时分配
  打个比方,单链表就像是散落的n个房间,每个房间的右边有走向下一个房间的方向指示,因此,如果想访问任何一个房间,都要从第一个房间开始,依次走过之前的每一个房间,才能到达指定的房间,而不能直接访问指定的房间,即链表不支持随机访问。
在这里插入图片描述
                     图2. 单链表的结构

2.单链表的特点

  每一个单链表都有一个头指针,它一般用于指向单链表的第一个结点。如果一个单链表有头结点,则头指针指向头结点;否则直接指向第一个存储信息的结点(尾指针可根据情况自行设置)。

(1)带头结点的单链表

  单链表的第一种类型是带头结点的单链表。头结点是一个特殊的结点,它不像其他结点一样存储数据,而是链表的一个标志,存储一些特殊的信息,如表长等。同时,带头结点的单链表的头指针始终不为空,当头结点的指针域为空时,链表为空。
在这里插入图片描述
                     图3. 带头结点的单链表

(2)不带头结点的单链表

  第二种类型就是不带头结点的单链表。在这种类型的单链表中,头指针直接指向存储用户信息的第一个链表,同时当头指针为空时,链表为空。
在这里插入图片描述
                     图4. 不带头结点的单链表

3.参考代码

(1)结点和头指针的定义

  首先,考虑到我们在进行结点的增删过程中,头指针的值会随时变化,我们选择定义一个全局的结构体指针变量,用来保存我们的头指针;同时,在结点结构体中,我们分别定义一个int类型的变量和一个结构体指针变量,分别作为我们的数据域和指针域(数据域可根据需要自行修改)。

PNode __Head = NULL;          //头指针

typedef struct NODE
{
	int BufferData;           //数据域
	struct NODE* Flink;       //指针域
}Node,*PNode;

(2)单链表的遍历

  单链表的遍历,就是从首个结点开始,逐个输出单链表中每个结点数据域的值。

void TravelList()
{
	//定义临时指针,用于遍历链表
	PNode v1 = __Head;
	//因为最后一个结点的指针域为空,所以我们以此作为循环条件来进行链表的遍历
	while (v1 != NULL)
	{
		printf("%d ", v1->BufferData);
		//每次遍历后,临时指针v1后移,指向下一结点,直至最后一个结点停止循环
		v1 = v1->Flink;
	}
	printf("\r\n");
}

(3)求单链表的表长

int GetListLength()
{
    //临时变量用于保存表长
	int ListLength = 0;
	//临时指针用于遍历链表
	PNode v1 = __Head;
	while (v1 != NULL)
	{
	    //每循环依次,表长+1,直至循环结束
		v1 = v1->Flink;
		ListLength++;
	}
	return ListLength;
}

(3)向单链表中插入结点

 ① 头插法

在这里插入图片描述
                       图5. 单链表的头插法

void FrontInsert(int Data)
{
    //由于插入新结点,需要动态申请一块内存
	PNode v1 = new Node;
	//对新申请的结构体进行初始化
	v1->Flink = NULL;
	v1->BufferData = Data;
	//如果链表为空,直接让头指针指向新结点
	if (__Head == NULL)
	{
		__Head = v1;
	}
	else
	{
	    //插入到首个结点之前
		v1->Flink = __Head;
		__Head = v1;
	}
}

 ② 尾插法

在这里插入图片描述
                       图6. 单链表的尾插法

void BackInsert(int Data)
{
    //动态申请一块内存并初始化结构体成员
	PNode v1 = new Node;
	v1->BufferData = Data;
	v1->Flink = NULL;
	//如果链表为空,直接让头指针指向该结点
	if (__Head == NULL)
	{
		__Head = v1;
	}
	else
	{
	    //定义临时指针,用于遍历链表找到最后最后一个结点来实现尾插法
		PNode v2 = __Head;
		while (v2->Flink != NULL)
		{
			v2 = v2->Flink;
		}
		//将新结点插到链表中
		v2->Flink = v1;
	}
}

 ③ 表中插入

在这里插入图片描述
                       图7. 单链表的表中插入

void MediumInsert(int Position, int Data)
{
    //首先获得单链表的表长
	int ListLength = GetListLength();
	//判断插入位置是否合法,非法则返回错误
	if (Position < 1 || Position > ListLength)
	{
		_tprintf(_T("Position Error!"));
	}
	else
	{
		PNode v1 = __Head;
		PNode v2 = new Node;
		v2->BufferData = Data;
		v2->Flink = NULL;
		//找到插入位置的前一个结点
		for (int i = 1; i < Position - 1; i++)
		{
			v1 = v1->Flink;
		}
		//这里如果先连接插入结点和其前驱结点,则前驱结点保存的插入结点的后继节点的地址将丢失
		//所以应先连接插入结点和其后继结点,再连接前驱结点
		v2->Flink = v1->Flink;
		v1->Flink = v2;
	}
}

(4)从单链表中查找数据

int FindData(int Data)
{
	PNode v1 = __Head;
	int Position = 1;
	while (v1 != NULL)
	{
	    //遍历单链表,如果结点数据域的值与所查值相等,则返回数据位置
		if (v1->BufferData == Data)
		{
			printf("Find Data %d Success!The Position is %d\r\n", Data, Position);
			return Position;
		}
		else
		{
		    //直到查找到链表最后一个结点
			v1 = v1->Flink;
			Position++;
		}
	}
	//当v1为空时,表示v1已经指向尾结点的后继结点,即已经查找完所有结点,此时返回查找失败
	if (v1 == NULL)
	{
		printf("Find Data %d Fail!\r\n",Data);
		return 0;
	}
	
}

(5)从单链表中删除结点

void DeleteData(int Data)
{
    //首先查找待删数据在单链表中的位置
	int Position = FindData(Data);
	if (Position == 0)
	{
	    //该数据不存在
		printf("Delete Data %d Fail!\r\n", Data);
		return;
	}
	else
	{
	    //查找待删结点的前驱节点
		PNode v1 = __Head;
		for (int i = 1; i < Position - 1; i++)
		{
			v1 = v1->Flink;
		}
		//v2为待删结点  直接让v2的前驱结点指向v2的后继结点
		PNode v2 = v1->Flink;
		v1->Flink = v2->Flink;
		 //释放内存
		delete(v2);                    
		printf("Delete Data %d Success!\r\n", Data);
	}
}

(6)main()函数

① 测试代码

void _tmain()
{
	BackInsert(5);
	BackInsert(128);
	BackInsert(66);
	FrontInsert(13);
	FrontInsert(25);
	FrontInsert(132);
	TravelList();
	MediumInsert(3, 92);
	MediumInsert(5, 88);
	TravelList();
	FindData(88);
	FindData(128);
	FindData(100);
	DeleteData(13);
	DeleteData(0);
	DeleteData(128);
	TravelList();
	return;
}

② 测试结果

在这里插入图片描述

(7)完整代码

#include<iostream>
using namespace std;

typedef struct NODE
{
	int BufferData;
	struct NODE* Flink;
}Node,*PNode;

PNode __Head = NULL;

void FrontInsert(int Data);
void BackInsert(int Data);
void MediumInsert(int Position, int Data);
int GetListLength();
void TravelList();
int FindData(int Data);
void DeleteData(int Data);

void _tmain()
{
	BackInsert(5);
	BackInsert(128);
	BackInsert(66);
	FrontInsert(13);
	FrontInsert(25);
	FrontInsert(132);
	TravelList();
	MediumInsert(3, 92);
	MediumInsert(5, 88);
	TravelList();
	FindData(88);
	FindData(128);
	FindData(100);
	DeleteData(13);
	DeleteData(0);
	DeleteData(128);
	TravelList();
	return;
}

void FrontInsert(int Data)
{
	PNode v1 = new Node;
	v1->Flink = NULL;
	v1->BufferData = Data;
	if (__Head == NULL)
	{
		__Head = v1;
	}
	else
	{
		v1->Flink = __Head;
		__Head = v1;
	}
}

void BackInsert(int Data)
{
	PNode v1 = new Node;
	v1->BufferData = Data;
	v1->Flink = NULL;
	if (__Head == NULL)
	{
		__Head = v1;
	}
	else
	{
		PNode v2 = __Head;
		while (v2->Flink != NULL)
		{
			v2 = v2->Flink;
		}
		v2->Flink = v1;
	}
}

void MediumInsert(int Position, int Data)
{
	int ListLength = GetListLength();
	if (Position < 1 || Position > ListLength)
	{
		_tprintf(_T("Position Error!"));
	}
	else
	{
		//找到插入位置的前一个结点
		PNode v1 = __Head;
		PNode v2 = new Node;
		v2->BufferData = Data;
		v2->Flink = NULL;
		for (int i = 1; i < Position - 1; i++)
		{
			v1 = v1->Flink;
		}
		v2->Flink = v1->Flink;
		v1->Flink = v2;
	}
}

int GetListLength()
{
	int ListLength = 0;
	PNode v1 = __Head;
	while (v1 != NULL)
	{
		v1 = v1->Flink;
		ListLength++;
	}
	return ListLength;
}

void TravelList()
{
	PNode v1 = __Head;
	while (v1 != NULL)
	{
		printf("%d ", v1->BufferData);
		v1 = v1->Flink;
	}
	printf("\r\n");
}

int FindData(int Data)
{
	PNode v1 = __Head;
	int Position = 1;
	while (v1 != NULL)
	{
		if (v1->BufferData == Data)
		{
			printf("Find Data %d Success!The Position is %d\r\n", Data, Position);
			return Position;
		}
		else
		{
			v1 = v1->Flink;
			Position++;
		}
	}
	if (v1 == NULL)
	{
		printf("Find Data %d Fail!\r\n",Data);
		return 0;
	}
}

void DeleteData(int Data)
{
	int Position = FindData(Data);
	if (Position == 0)
	{
		printf("Delete Data %d Fail!\r\n", Data);
		return;
	}
	else
	{
		PNode v1 = __Head;
		for (int i = 1; i < Position - 1; i++)
		{
			v1 = v1->Flink;
		}
		PNode v2 = v1->Flink;
		v1->Flink = v2->Flink;
		delete(v2);
		printf("Delete Data %d Success!\r\n", Data);
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 基于链表实现树结构 */ package dsa; public class TreeLinkedList implements Tree { private Object element;//树根节点 private TreeLinkedList parent, firstChild, nextSibling;//父亲、长子及最大的弟弟 //(单节点树)构造方法 public TreeLinkedList() { this(null, null, null, null); } //构造方法 public TreeLinkedList(Object e, TreeLinkedList p, TreeLinkedList c, TreeLinkedList s) { element = e; parent = p; firstChild = c; nextSibling = s; } /*---------- Tree接口中各方法的实现 ----------*/ //返回当前节点中存放的对象 public Object getElem() { return element; } //将对象obj存入当前节点,并返回此前的内容 public Object setElem(Object obj) { Object bak = element; element = obj; return bak; } //返回当前节点的父节点;对于根节点,返回null public TreeLinkedList getParent() { return parent; } //返回当前节点的长子;若没有孩子,则返回null public TreeLinkedList getFirstChild() { return firstChild; } //返回当前节点的最大弟弟;若没有弟弟,则返回null public TreeLinkedList getNextSibling() { return nextSibling; } //返回当前节点后代元素的数目,即以当前节点为根的子树的规模 public int getSize() { int size = 1;//当前节点也是自己的后代 TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 size += subtree.getSize();//累加 subtree = subtree.getNextSibling();//所有孩子的后代数目 } return size;//即可得到当前节点的后代总数 } //返回当前节点的高度 public int getHeight() { int height = -1; TreeLinkedList subtree = firstChild;//从长子开始 while (null != subtree) {//依次 height = Math.max(height, subtree.getHeight());//在所有孩子中取最大高度 subtree = subtree.getNextSibling(); } return height+1;//即可得到当前节点的高度 } //返回当前节点的深度 public int getDepth() { int depth = 0; TreeLinkedList p = parent;//从父亲开始 while (null != p) {//依次 depth++; p = p.getParent();//访问各个真祖先 } return depth;//真祖先的数目,即为当前节点的深度 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值