一.引入
静态链表是用数组来表示单链表,用数组元素的下表来模拟单链表的指针。静态链表的每个数组元素由两个域构成: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;
}
五.运行结果