C++基于数组存储结构的链表设计与实现

基于数组存储结构的链表

  • 数组中的每一个元素附加一个指向后续元素的指针,对数组元素的访问顺序由指针指向的位置决定,这样可以不改变各个元素的物理位置,就可以改变数组的逻辑顺序

  • 这是一种基于数组的存储结构,倘若运算过程中数组的大小不发生改变则称之为静态链表,其实也可以改造为动态数组来实现

  • 基于数据存储结构的链表的每个元素包含: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版》,清华大学出版社

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值