数据结构之静态链表

本文介绍静态链表的概念及其实现方式,包括使用数组模拟单链表的指针,详细讲解了静态链表的各种操作算法,如插入、删除、更新等,并提供了完整的C++代码实现及测试案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.引入

        静态链表是用数组来表示单链表,用数组元素的下表来模拟单链表的指针。静态链表的每个数组元素由两个域构成:data 域存放数组元素,next 域存放该元素的后继元素所在的数组下标。由于它是利用数组定义的,属于静态存储分配,因此叫做静态链表。

二.算法设计

StatLinkedList.h

#ifndef SRC_STATLINKEDLIST_H_
#define SRC_STATLINKEDLIST_H_
/*
 * 结点:
 * data:存储数据元素
 * next:存储该元素的后继结点角标
 */
template <class T>
struct Node{
	T data;
	int next;
};

template <class T>
class StatLinkedList {
public:
	StatLinkedList();//无参构造器
	StatLinkedList(T array[],int length);//带参构造器
	virtual ~StatLinkedList();//空的析构函数
	int getLength();//返回当前长度
	void getAll();//遍历当前数组
	T getByIndex(int index);//通过角标获得元素
	void getByValue(T value);//通过值获得该元素所在的位置
	void insert(int index,T value);//插入:可以实现在头部、元素间和尾部插入。
	void removeByIndex(int index);//通过角标移除元素
	void removeByValue(T value);//通过值移除元素:只能移除首次出现的那个
	void updateByIndex(int index,T value);//通过角标修改元素的值
	void updateByValue(T oldValue,T newValue);//通过值修改元素的值

private:
	static const int MAX_LENGTH = 30;//静态链表的最大长度
	Node<T> node[MAX_LENGTH];//节点
	Node<T> *first;//指向数据链头指针
	int avail;//指向空闲链的头指针
	int length;//当前静态链表的长度
	bool checkIndex(int index);//检查角标是否合法

};

#endif

三.详细设计(C++)

StatLinkedList.cpp

#include "StatLinkedList.h"
#include <iostream>
using namespace std;
/*
 * 检查角标:
 * 角标小于0或大于等于最大长度和当前长度都视为非法,返回 false ,否则返回 true。
 */
template <class T>
bool StatLinkedList<T>::checkIndex(int index){
	if(index < 0){
		cout<<"角标不能小于0!"<<endl;
		return false;
	}
	if(index >= MAX_LENGTH){
		cout<<"角标越界,最大长度为:"<<MAX_LENGTH<<"!"<<endl;
		return false;
	}
	if(index >= length){
		cout<<"角标越界,当前长度为:"<<length<<"!"<<endl;
		return false;
	}
	return true;
}
/*
 * 无参构造器:
 * 1.初始化静态链表,长度为 MAX_LENGTH ,由 MAX_LENGTH 个结点组成,结点的 next 依次赋值,最后一个节点的 next 赋值为 -1,
 *   即1,2,...,MAX_LENGTH-1,-1
 *   当前的静态链表只有空闲链,不存在数据链
 * 2.avail 指向空闲链的头部
 * 3.摘下空闲链的头部 avail 赋给数据链的头指针 first
 * 4.记录下一个空闲节点的角标
 * 5.因为 first 后没有元素,故 next 赋值为 -1 ,即不指向任何节点
 * 6.avail 移到下一个空闲节点
 * 7.长度为 0
 */
template <class T>
StatLinkedList<T>::StatLinkedList(){
	for(int i = 0;i < MAX_LENGTH;i++){
		node[i].next = i+1;
		if(i == MAX_LENGTH-1){
			node[i].next = -1;
		}
	}
	avail = 0;
	first = &node[avail];
	int nextFreeNodeIndex = node[avail].next;
	first->next = -1;
	avail = nextFreeNodeIndex;
	length = 0;
}
/*
 * 带参构造器:
 * 1.如果传入的数组的长度大于静态链表的最大长度,返回,否则
 * 2.初始化静态链表
 * 3.avail 指向空闲链的头部
 * 4.摘下空闲链的头部 avail 赋给数据链的头指针 first
 * 5.avail 记录下一个空闲节点的角标
 * 6.初始化数据链,摘下空闲链的头部 avail ,把元素存储在 data 中,avail 移到下一个空闲节点
 *   若到数组的最后一个元素了,则先记录当前节点的 next ,即下一个空闲节点的角标,再把当前节点的 next 赋值为 -1 ,表示是数组的最后一个元素,
 *   再把记录的下一个空闲节点的角标赋给 avail
 * 7.记录当前长度
 */
template <class T>
StatLinkedList<T>::StatLinkedList(T array[],int length){
	if(length > MAX_LENGTH){
		cout<<"长度过大,最大长度为:"<<MAX_LENGTH<<"!"<<endl;
		return;
	}
	for(int i = 0;i < MAX_LENGTH;i++){
		node[i].next = i+1;
		if(i == MAX_LENGTH-1){
			node[i].next = -1;
		}
	}
	avail = 0;
	first = &node[avail];
	avail = node[avail].next;
	for(int i = 0;i < length;i++){
		node[avail].data = array[i];
		if(i == length-1){
			int nextFreeNodeIndex = node[avail].next;
			node[avail].next = -1;
			avail = nextFreeNodeIndex;
		}else{
			avail = node[avail].next;
		}
	}
	this->length = length;
}
/*
 * 空的析构函数
 */
template <class T>
StatLinkedList<T>::~StatLinkedList(){
}
/*
 * 返回当前长度
 */
template <class T>
int StatLinkedList<T>::getLength(){
	return length;
}
/*
 * 遍历当前数组:
 * 1.定义一个指针 p 指向数据链的头指针 first
 * 2.p 根据 first 的 next 找到存储第一个元素的节点,并指向该节点,输出节点的 data
 *   若当前结点的 next 不等于 -1 ,即代表后面还有元素 ,则 p 通过所在节点的 next 找到存储下一个元素的节点并指向它,输出 data ,直到当前结点的 next 为 -1
 */
template <class T>
void StatLinkedList<T>::getAll(){
	Node<T> *p = first;
	int index = 0;
	while(p->next != -1){
		p = &node[p->next];
		if(index == 0){
			cout<<"["<<p->data<<",";
		}else if(index == length-1){
			cout<<p->data<<"]"<<endl;
		}else{
			cout<<p->data<<",";
		}
		index++;
	}
}
/*
 * 通过角标获得元素:
 * 1.判断角标是否合法,若不合法返回 -1 ,否则
 * 2.定义一个指针 p 指向数据链的头指针 first
 * 3.遍历,直到 p 到达该角标所在的节点
 * 4.返回该节点的 data
 */
template <class T>
T StatLinkedList<T>::getByIndex(int index){
	if(!checkIndex(index)){
		return -1;
	}
	Node<T> *p = first;
	for(int i = 0;i <= index;i++){
		p = &node[p->next];
	}
	return p->data;
}
/*
 * 通过值获得该元素所在的位置:
 * 1.定义一个标记 flag,初始化为 false ,用来记录是否找到该值
 * 2.定义一个角标 index ,初始化为 0 ,用来记录遍历到第几个元素
 * 3.定义一个指针 p 指向数据链的头指针 first
 * 4.遍历,判断当前节点的 data 是否等于 value ,若是,输出当前的 index 并把 flag 赋值为 true
 *   每到达下一个节点,index++
 * 5.若找不到 data 与 value 相等,输出“没有此值!”
 */
template <class T>
void StatLinkedList<T>::getByValue(T value){
	bool flag = false;
	int index = 0;
	Node<T> *p = first;
	while(p->next != -1){
		p = &node[p->next];
		if(p->data == value){
			cout<<"arr["<<index<<"]="<<p->data<<" ";
			flag = true;
		}
		index++;
	}
	if(!flag){
		cout<<"没有此值!"<<endl;
	}
}
/*
 * 插入:可以实现在头部、元素间和尾部插入。
 * 1.判断角标是否合法,若不合法返回 ,否则
 * 2.定义一个指针 p 指向数据链的头指针 first
 * 3.遍历,移动指针 p 到插入元素的前一个元素的节点处,
 *   如原来[1,2,3],要在1和2之间插入,则移到1所在的结点处
 * 4.定义一个变量 nextFreeNodeIndex 存储当前空闲结点的 next ,即下一个空闲结点的角标
 * 5.根据 avail 的值获得空闲链的第一个结点 a,并把 value 存储在结点 a 的 data
 * 6.把指针 p 指向的结点的 next 赋给结点 a 的 next,即新插入元素可根据自己的 next 找到自己的下一个元素所在的结点
 * 7.把节点a的所在结点的值 avail 即位置,赋给指针 p 指向的结点的 next,即新插入元素的前一个元素可根据自己的 next 找到新插入元素所在的结点
 * 8.把下一个空闲结点的角标 nextFreeNodeIndex 赋给avail
 *   注意:不能写成++,因为如果移除了一个元素后,那么原先存储该元素的节点就不能再利用了
 * 9.长度加一
 */
template <class T>
void StatLinkedList<T>::insert(int index,T value){
	if(index == length){
		goto A;
	}else if(!checkIndex(index)){
		return;
	}
	A:Node<T> *p = first;
	for(int i = 0;i < index;i++){
		p = &node[p->next];
	}
	int nextFreeNodeIndex = node[avail].next;
	node[avail].data = value;
	node[avail].next = p->next;
	p->next = avail;
	avail = nextFreeNodeIndex;
	length++;
}
/*
 * 通过角标移除元素:如:[1,2,3,4],移除2
 * 1.判断角标是否合法,若不合法返回 ,否则
 * 2.定义一个指针 p 指向数据链的头指针 first
 * 3.遍历,移动指针 p 到移除元素的前一个元素的结点处,即1处
 * 4.定义一个变量 FreeNodeIndex 存储当前结点的 next ,即存储2所在的结点的角标
 * 5.定义一个指针 removeNode ,将其指向移除的结点,即2处
 * 6.把 removeNode 的 next 赋给 p 的 next ,即把存储3的结点的角标赋给存储1的结点的 next ,此时存储1的结点可根据自己的 next 找到存储3所在的结点
 * 7.把 avail 赋给 removeNode 的 next ,即移除的结点成为空闲链的第二个结点
 * 8.把 FreeNodeIndex 赋给 avail,即 avail 指向移除的结点,若下一次要插入元素时则在此位置插入
 */
template <class T>
void StatLinkedList<T>::removeByIndex(int index){
	if(!checkIndex(index)){
		return;
	}
	Node<T> *p = first;
	for(int i = 0;i < index;i++){
		p = &node[p->next];
	}
	int freeNodeIndex = p->next;
	Node<T> *removeNode = &node[p->next];
	p->next = removeNode->next;
	removeNode->next = avail;
	avail = freeNodeIndex;
	length--;
}
/*
 * 通过值移除元素:只能移除首次出现的那个
 * 1.定义一个标记 flag,初始化为 false ,用来记录是否找到该值
 * 2.定义一个角标 index ,初始化为 0 ,用来记录遍历到第几个元素
 * 3.定义一个指针 p 指向数据链的头指针 first
 * 4.遍历,判断当前节点的 data 是否等于 value ,若是,调用 removeByIndex(int index) 方法,把当前的 index 作为实参,把  flag 赋值为 true ,跳出循环
 *   每到达下一个节点,index++
 * 5.若找不到 data 与 value 相等,输出“没有此值!”
 */
template <class T>
void StatLinkedList<T>::removeByValue(T value){
	bool flag = false;
	int index = 0;
	Node<T> *p = first;
	while(p->next != -1){
		p = &node[p->next];
		if(p->data == value){
			removeByIndex(index);
			flag = true;
			break;
		}
		index++;
	}
	if(!flag){
		cout<<"没有此值!"<<endl;
	}
}
/*
 * 通过角标修改元素的值:
 * 1.判断角标是否合法,若不合法返回 ,否则
 * 2.定义一个指针 p 指向数据链的头指针 first
 * 3.遍历,移动指针 p 到要修改的元素的结点处
 * 4.把 value 赋给该结点的 data
 */
template <class T>
void StatLinkedList<T>::updateByIndex(int index,T value){
	if(!checkIndex(index)){
		return;
	}
	Node<T> *p = first;
	for(int i = 0;i <= index;i++){
		p = &node[p->next];
	}
	p->data = value;
}
/*
 * 通过值修改元素的值:
 * 1.定义一个标记 flag,初始化为 false ,用来记录是否找到该值
 * 2.定义一个指针 p 指向数据链的头指针 first
 * 3.遍历,判断当前节点的 data 是否等于 oldValue ,若是,把 newValue 赋给当前节点的 data ,并把 flag 赋值为 true
 * 4.若找不到 data 与 value 相等,输出“没有此值!”
 */
template <class T>
void StatLinkedList<T>::updateByValue(T oldValue,T newValue){
	bool flag = false;
	Node<T> *p = first;
	while(p->next != -1){
		p = &node[p->next];
		if(p->data == oldValue){
			p->data = newValue;
			flag = true;
		}
	}
	if(!flag){
		cout<<"没有此值!"<<endl;
	}
}


四.测试

TestStatLinkedList.cpp

#include "StatLinkedList.h"
#include "StatLinkedList.cpp"
#include <iostream>
using namespace std;
int main(int argc, char **argv) {
	int arr[] = {0,1,2,3,4,5,4,3,2,1,0};
	cout<<"创建对象:"<<endl;
	StatLinkedList<int> statLinkedList(arr,11);
	cout<<"当前长度:"<<statLinkedList.getLength()<<endl;
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	cout<<"获得角标为6的值:"<<statLinkedList.getByIndex(6)<<endl;
	cout<<endl;

	cout<<"获得值为0的元素的位置:"<<endl;
	statLinkedList.getByValue(0);
	cout<<endl<<endl;

	cout<<"在角标为6处插入6:"<<endl;
	statLinkedList.insert(6,6);
	cout<<"当前长度:"<<statLinkedList.getLength()<<endl;
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	cout<<"移除角标为6的元素:"<<endl;
	statLinkedList.removeByIndex(6);
	cout<<"当前长度:"<<statLinkedList.getLength()<<endl;
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	cout<<"移除值为0的元素:(只能移除首次出现的)"<<endl;
	statLinkedList.removeByValue(0);
	cout<<"当前长度:"<<statLinkedList.getLength()<<endl;
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	cout<<"把角标为4的元素的值修改为4:"<<endl;
	statLinkedList.updateByIndex(4,4);
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	cout<<"把值为4的元素的值修改为0:"<<endl;
	statLinkedList.updateByValue(4,0);
	cout<<"遍历对象:";
	statLinkedList.getAll();
	cout<<endl;

	return 0;
}

五.运行结果



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值