基于数组存储结构的链表
-
数组中的每一个元素附加一个指向后续元素的指针,对数组元素的访问顺序由指针指向的位置决定,这样可以不改变各个元素的物理位置,就可以改变数组的逻辑顺序
-
这是一种基于数组的存储结构,倘若运算过程中数组的大小不发生改变则称之为静态链表,其实也可以改造为动态数组来实现
-
基于数据存储结构的链表的每个元素包含:
data
数据和link
指针域优点:对数组元素的插入、删除操作只需要修改链接指针而不需要移动元素,因此具有更高的效率
缺点:查找元素需要从头部开始逐个遍历,随机存取较慢
基于数组存储结构的链表设计
链表中的每一个元素的指针域都存放着指向下一个元素的位置,最后一个元素的指针域为-1标记结尾
#include <iostream>
#include <cassert>
using namespace std;
template <class T>
struct Element {
//数据域
T data;
//指针
int link;
Element() : link(-1) {}
Element(T x, int next=-1) : data(x), link(next) {}
//输出运算符
friend ostream &operator<<(ostream &out,Element &el){
out<<el.data;
}
};
template <class T>
class StaticLinkList {
private:
//元素elements[0]为附加头节点,elements[0].link始终指向第一个元素,若没有元素则elements[0].link为-1
//数组elements[currentSize].link始终为-1表示为最后一个元素
Element<T> *elements;
//最大元素个数、当前元素个数
int maxSize,currentSize;
//链表尾指针,用于顺序插入,空链表时为-1
int tail;
//avail第一个一个可放置元素的指针(或空闲链表的头指针),多个的元素形成一个备用链表
//添加元素时,分配一个空间使用,然后就将avail指向下一个;删除元素时,所删除元素添加到avail指针所指示备用链表中
int avail;
public:
StaticLinkList(int sz);
//补充:改造为动态链表,空间不够时扩展为原来的0.75倍
void expandSpace();
//添加元素,默认尾部添加
void add(T x);
//定位到第k个元素的位置,k = 0指向的是附加头节点,k=1才指向第一个元素
int locate(int k);
//查找元素x,找到则返回指针,否则返回-1
int find(T x);
//删除第k个元素,先定位到第k-1个元素
bool remove(int k);
//第k个元素的后面插入元素x
bool insert(int k, T x);
//链表大小
int size(){
return this->currentSize;
}
//输出有效元素
void output();
//查看备用链表
void availableSpace();
//取出第k个元素
T get(int k);
//修改第k个元素的取值
void set(int k, T x);
Element<T>& operator[](int pos){
return elements[pos];
}
};
构造函数
初始化当前元素的个数currentSize
、可容纳的最大个数maxSize
、指示尾元素的指针tail
avail
指向空闲链表(备用链表)的头部
template <class T>
StaticLinkList<T>::StaticLinkList(int sz) {
currentSize = 0;
maxSize = sz < 10 ? 10 : sz;
tail = -1;
//分配sz+1个存储空间,elements[0]指向第一个元素的指针
elements = new Element<T>[sz + 1];
elements[0].link = -1;
//空闲链表指针从1开始
this->avail = 1;
for (int i = 1; i < sz; i++) {
elements[i].link = i + 1;
}
//结尾标记
elements[this->maxSize].link = -1;
}
添加元素
设置尾指针指示最后一个添加的元素,每次添加时只需要将新元素链接到尾指针之后
新添加一个元素就从备用链表的表头摘除一个元素,备用链表头指针指向下一个
template <class T>
void StaticLinkList<T>::add(T x) {
if (currentSize == maxSize) {
// cerr<<"element maxSize overflow"<<endl;
// return;
expandSpace();
}
int cur = avail;
//备用链表指针指向下一个
avail = elements[avail].link;
if (currentSize == 0) {
elements[0].link = cur;
} else {
//尾部链接新节点的指针
elements[tail].link = cur;
}
elements[cur].data = x;
elements[cur].link = -1;
// tail指向链尾
tail = cur;
currentSize++;
}
定位某个元素
//定位第k个元素,找到则返回物理位置,否则返回-1
template <class T>
int StaticLinkList<T>::locate(int k) {
if (k < 0 || k > currentSize) {
return -1;
}
if (k == 0) {
return 0;
}
int pos = elements[0].link;
int cnt = 1;
while (pos != -1 && cnt < k) {
cnt++;
pos = elements[pos].link;
}
return pos;
}
插入元素
在某个位置插入新元素,需要先从备用链表中摘下第一个元素,将其作为新元素
然后在定位到指定位置后链入新元素的指针
//在第k个位置之后插入新元素
template <class T>
bool StaticLinkList<T>::insert(int k, T x) {
if (currentSize == maxSize) {
expandSpace();
// return false;
}
int pos = locate(k);
if (pos == -1) {
return false;
}
int cur = avail;
//移动到下一个
avail = elements[avail].link;
elements[cur].data = x;
// cur的后继节点链接pos的后继节点
elements[cur].link = elements[pos].link;
// pos的后继节点链接cur指针
elements[pos].link = cur;
// tail指向最后一个节点
tail = cur;
currentSize++;
return true;
}
删除元素
删除第k
个元素,需要先找到第k-1
个元素,将删除的元素从第k-1
个元素中摘除,并将其链入备用链表头部
template <class T>
bool StaticLinkList<T>::remove(int k) {
if (k < 0 || k > currentSize) {
return false;
}
int pre = locate(k - 1);
if (pre == -1) {
return false;
}
//删除的元素指针
int cur = elements[pre].link;
//摘下elements[cur]
elements[pre].link = elements[cur].link;
// cur成为备用链表的头指针
elements[cur].link = avail;
avail = cur;
//删除的是最后一个元素,则修改tail指针
if (tail == cur) {
//当pre为0时,表示此次删除后链表为空,tail置为0
tail = pre == 0 ? -1 : pre;
}
currentSize--;
return true;
}
查找元素
查找一个给定的元素,需要从第一个元素开始,依次遍历比较是否相等
若找到返回元素所在位置,否则返回-1
template <class T>
int StaticLinkList<T>::find(T x) {
int cur = elements[0].link;
while (cur != -1) {
if (elements[cur].data == x) {
break;
} else {
cur = elements[cur].link;
}
}
return cur;
}
扩充存储空间
若当前存储元素已达到最大个数,可以考虑创建一个比原来大0.75
倍的新数组作为存储空间
将原来的数据按顺序复制到新数组,并将旧数组删除,指向新数组,并修改备用链表头指针
template <class T>
void StaticLinkList<T>::expandSpace() {
cout << "expandSpace...." << endl;
maxSize += maxSize * 0.75;
Element<T> *arrs = new Element<T>[maxSize + 1];
int cur = elements[0].link;
int k = 1;
arrs[0].link = k;
while (cur != -1) {
arrs[k].data = elements[cur].data;
arrs[k].link = k + 1;
k = k + 1;
cur = elements[cur].link;
}
//最后一个元素指针
tail = k - 1;
arrs[tail].link = -1;
//备用链表头指针
avail = k;
while (k < maxSize) {
arrs[k].link = k + 1;
k++;
}
//备用链表尾部
arrs[k].link = -1;
delete[] elements;
this->elements = arrs;
}
顺序遍历
从附加头元素开始,依次沿每个元素的指针指向遍历,指针指向-1表示结束
template <class T>
void StaticLinkList<T>::output() {
int pos = 0;
pos = elements[pos].link;
while (pos != -1) {
cout << elements[pos] << ",";
pos = elements[pos].link;
}
cout << endl;
cout << "maxSize=" << maxSize << ", ";
cout << "size=" << currentSize << ", tail=" << tail
<< ", elements[tail]=" << elements[tail] << endl
<< endl;
}
完整源码
#include <iostream>
#include <cassert>
using namespace std;
template <class T>
struct Element {
//数据域
T data;
//指针
int link;
Element() : link(-1) {}
Element(T x, int next=-1) : data(x), link(next) {}
//输出运算符
friend ostream &operator<<(ostream &out,Element &el){
out<<el.data;
}
};
template <class T>
class StaticLinkList {
private:
//元素elements[0]为附加头节点,elements[0].link始终指向第一个元素,若没有元素则elements[0].link为-1
//数组elements[currentSize].link始终为-1表示为最后一个元素
Element<T> *elements;
//最大元素个数、当前元素个数
int maxSize,currentSize;
//链表尾指针,用于顺序插入,空链表时为-1
int tail;
//avail第一个一个可放置元素的指针(或空闲链表的头指针),多个的元素形成一个备用链表
//添加元素时,分配一个空间使用,然后就将avail指向下一个;删除元素时,所删除元素添加到avail指针所指示备用链表中
int avail;
public:
StaticLinkList(int sz);
//补充:改造为动态链表,空间不够时扩展为原来的0.75倍
void expandSpace();
//添加元素,默认尾部添加
void add(T x);
//定位到第k个元素的位置,k = 0指向的是附加头节点,k=1才指向第一个元素
int locate(int k);
//查找元素x,找到则返回指针,否则返回-1
int find(T x);
//删除第k个元素,先定位到第k-1个元素
bool remove(int k);
//第k个元素的后面插入元素x
bool insert(int k, T x);
//链表大小
int size(){
return this->currentSize;
}
//输出有效元素
void output();
//查看备用链表
void availableSpace();
//取出第k个元素
T get(int k);
//修改第k个元素的取值
void set(int k, T x);
Element<T>& operator[](int pos){
return elements[pos];
}
};
template <class T>
StaticLinkList<T>::StaticLinkList(int sz) {
currentSize = 0;
maxSize = sz < 10 ? 10 : sz;
tail = -1;
//分配sz+1个存储空间,elements[0]指向第一个元素的指针
elements = new Element<T>[sz + 1];
elements[0].link = -1;
//空闲链表指针从1开始
this->avail = 1;
for (int i = 1; i < sz; i++) {
elements[i].link = i + 1;
}
elements[this->maxSize].link = -1;
availableSpace();
}
template <class T>
void StaticLinkList<T>::expandSpace() {
cout << "expandSpace...." << endl;
maxSize += maxSize * 0.75;
Element<T> *arrs = new Element<T>[maxSize + 1];
int cur = elements[0].link;
int k = 1;
arrs[0].link = k;
while (cur != -1) {
arrs[k].data = elements[cur].data;
arrs[k].link = k + 1;
k = k + 1;
cur = elements[cur].link;
}
//最后一个元素指针
tail = k - 1;
arrs[tail].link = -1;
//备用链表头指针
avail = k;
while (k < maxSize) {
arrs[k].link = k + 1;
k++;
}
//备用链表尾部
arrs[k].link = -1;
delete[] elements;
this->elements = arrs;
}
template <class T>
void StaticLinkList<T>::add(T x) {
if (currentSize == maxSize) {
// cerr<<"element maxSize overflow"<<endl;
// return;
expandSpace();
}
int cur = avail;
avail = elements[avail].link;
if (currentSize == 0) {
elements[0].link = cur;
} else {
//尾部链接新节点的指针
elements[tail].link = cur;
}
elements[cur].data = x;
elements[cur].link = -1;
// tail指向链尾
tail = cur;
currentSize++;
}
template <class T>
int StaticLinkList<T>::locate(int k) {
if (k < 0 || k > currentSize) {
return -1;
}
if (k == 0) {
return 0;
}
int pos = elements[0].link;
int cnt = 1;
while (pos != -1 && cnt < k) {
cnt++;
pos = elements[pos].link;
}
return pos;
}
template <class T>
bool StaticLinkList<T>::remove(int k) {
if (k < 0 || k > currentSize) {
return false;
}
int pre = locate(k - 1);
if (pre == -1) {
return false;
}
//删除的元素指针
int cur = elements[pre].link;
//摘下elements[cur]
elements[pre].link = elements[cur].link;
// cur成为备用链表的头指针
elements[cur].link = avail;
avail = cur;
//删除的是最后一个元素,则修改tail指针
if (tail == cur) {
//当pre为0时,表示此次删除后链表为空,tail置为0
tail = pre == 0 ? -1 : pre;
}
currentSize--;
return true;
}
template <class T>
int StaticLinkList<T>::find(T x) {
int cur = elements[0].link;
while (cur != -1) {
if (elements[cur].data == x) {
break;
} else {
cur = elements[cur].link;
}
}
return cur;
}
template <class T>
bool StaticLinkList<T>::insert(int k, T x) {
if (currentSize == maxSize) {
expandSpace();
// return false;
}
int pos = locate(k);
if (pos == -1) {
return false;
}
int cur = avail;
//移动到下一个
avail = elements[avail].link;
elements[cur].data = x;
// cur的后继节点链接pos的后继节点
elements[cur].link = elements[pos].link;
// pos的后继节点链接cur指针
elements[pos].link = cur;
// tail指向最后一个节点
tail = cur;
currentSize++;
return true;
}
template <class T>
void StaticLinkList<T>::output() {
int pos = 0;
pos = elements[pos].link;
while (pos != -1) {
cout << elements[pos] << ",";
pos = elements[pos].link;
}
cout << endl;
cout << "maxSize=" << maxSize << ", ";
cout << "size=" << currentSize << ", tail=" << tail
<< ", elements[tail]=" << elements[tail] << endl
<< endl;
}
template <class T>
void StaticLinkList<T>::availableSpace() {
cout << "availableSpace..." << endl;
int pos = avail;
while (pos != -1) {
cout << "[" << pos << "]_" << elements[pos] << "-->";
pos = elements[pos].link;
}
cout << endl;
}
template <class T>
T StaticLinkList<T>::get(int k) {
int pos = locate(k);
assert(pos != -1);
return elements[pos].data;
}
template <class T>
void StaticLinkList<T>::set(int k, T x) {
int pos = locate(k);
assert(pos != -1);
elements[pos].data = x;
}
int main() {
StaticLinkList<int> p(10);
int arr[] = {8,2,5,3,7,1,4,3,9,6,-11,223,444,345};
int sz = sizeof(arr)/sizeof(int);
cout<<"----add----"<<endl;
for(int i=0;i<sz;i++){
p.add(arr[i]);
}
p.output();
int pos = p.locate(2);
cout<<"pos="<<pos<<", p[pos]="<<p[pos]<<endl;
cout<<"----remove----"<<endl;
int deleted[]={10,9,8,2,1,6,3,1,1,1,1,1};
for(int i=0;i<sizeof(deleted)/sizeof(int);i++){
cout<<"del ---> pos="<<deleted[i]<<", success="<<p.remove(deleted[i])<<endl;
p.output();
}
for (int i = 0; i < sz; i++) {
p.add(arr[i]);
}
p.output();
cout<<"----insert----"<<endl;
int inserted[]={222,100,555,33};
for (int i = 0; i < sizeof(inserted)/sizeof(int); i++) {
cout<<"insert ---> pos="<<i<<", x="<<inserted[i]<<", success="<<p.insert(i,inserted[i])<<endl;
p.output();
}
int x = 3459;
int index = p.find(x);
cout << "find ==> index=" << index << ", p[pos]=" << p[index] << endl;
p.availableSpace();
}
参考资料
[1]
殷人昆,数据结构(用面向对象方法与C++语言描述)第2版》,清华大学出版社