线性表的间接寻址方法与单链表十分相似,同时也与顺序表相似,间接寻址分别继承了他们的优缺点。
实现方法,在单链表的基础上增加一组Node指针型的数组,数组的长度由常量MaxSize指定。
成员函数优化:
- 插入和删除时需要移动数组中的元素(相当于顺序表中的插入和删除,说不同的就是这里移动的是指针,顺序表移动的是元素)
- 查找指定位置元素时直接将参数作为数组下标然后就可以找到指定位置的元素(类似于顺序表),返回即可
上代码:
//节点
#pragma once
template< typename T>
class Node {
public:
T data; //数据存储域
Node *next; //指针域
};
类模板的实现:
#pragma once
#include<iostream>
#include"Node.h"
using namespace std;
const int MaxSize = 100; //常量指定数组长度
template<typename T>
class LinkList
{
public:
LinkList(T a[], int n); //有参构造函数 使用了头插法
~LinkList(); //析构函数
int Length(); //返回单链表的长度
T Get(int i); //按位查找,查找第i个节点的元素
int Locate(T x); //按值查找,查找链表中第一个值为x的元素,并返回序号
bool Insert(int i, T x); //插入元素,在第i个位置插入值x
bool Delete(int i); //删除节点,删除第i个节点
bool InsertHead(T x); //头插法插入节点
bool InsertTail(T x); //尾插法插入节点
void ListTraverse(); //遍历节点
bool changeList(int i, T x); //改变某一结点的值 i为节点的位置,x为替换的值
private:
Node<T> *first;
int m_Length; //实际使用过程当中,添加多一个数据成员Length会更好
Node<T> *address[MaxSize]; //Node指针型的数组
};
template<typename T>
LinkList<T>::LinkList(T a[], int n) //有参构造函数, 使用头插法(注意点:头插法是将元素放在头结点的后面)
{
if (n > MaxSize) {
throw string("数组长度长于链长度,录入失败");
exit(1);
}
first = new Node<T>; //空链表的初始化,同无参构造函数
first->next = NULL;
for (int i = 0; i<n; i++)
{
Node<T> *s = new Node<T>;
s->data = a[i];
s->next = first->next;
first->next = s;
}
m_Length = n;
Node<T> *p =first; /*临时指针*/
for (int j = 0; j < m_Length;j++) { /*将指针地址存入数组*/
p = p->next;
address[j] = p;
}
/*for (int i = 0; i < n; i++) //直接调用下面的头插法插入数据更方便!
{
InsertHead(a[i]);
}*/
}
//template<typename T>
//LinkList::LinkList(T a[], int n) //同样是有参构造函数,但是使用的是尾插法
//{
// Node<T> *first = new NOde<T>;
// Node<T> *r = first; //将头指针赋值给变量r
// for (int i = 0; i < n; i++)
// {
// s = new Node;
// s->data = a[i]; //创建新节点,赋值
// r->next = s; //将r的指针指向s
// r = s; //变量r后移到最后一个节点
// }
// r->next = NULL; //将尾节点的指针置空
//}
template<typename T>
bool LinkList<T>::InsertHead(T x) //头插发插入数据
{
if (m_Length>=MaxSize) {
throw string("链满,无法插入数据");
}
Node<T> *Temp = first->next; //建立指向头指针的临时变量
Node<T> *s = new Node<T>; //建立新节点
if (s == NULL) //判断新节点是否申请成功
{
return false;
}
s->data = x; //赋值
s->next = Temp; //上链
first->next = s;
m_Length++; //链表的长度加一
for (int i = m_Length-1; i > 0;i--) { /*间接寻址数组地址的改变(增加地址)*/
address[i] = address[i - 1];
}
address[0] = first->next;
return true; //插入成功,返回true
}
template<typename T>
bool LinkList<T>::InsertTail(T x) //使用尾插法插入数据,使数据插入到最后一个
{
if (m_Length >= MaxSize) {
throw string("链满,无法插入数据");
}
Node<T> *p = first; //建立临时遍历指针
Node<T> *s = new Node<T>; //建立新节点
if (s == NULL) //判断新节点是否申请成功,若申请失败,则退出函数,不插入数据
{
return false;
}
s->data = x;
while (p->next != NULL) //遍历指针,使临时指针指向尾节点
{
p = p->next;
}
p->next = s; //尾节点重新导向,将新节点上链
s->next = NULL; //将上链后的尾节点的指针域指向空
m_Length++;
address[m_Length - 1] = s; //增加间接寻址地址
return true; //返回true,插入成功
}
template<typename T>
LinkList<T>::~LinkList() //析构函数
{
while (first != NULL)
{
Node<T> *q = first; //遍历删除头指针指向的节点,将头指针暂存
first = first->next; //将头指针后移
delete q; //从链表中脱离出来的指针删除,释放内存
}
m_Length = 0;
}
template<typename T>
int LinkList<T>::Length() /*返回链表的长度的算法,实现思想:设定循环函数,将节点的指针从头指针开始依次后移,
后移一次便将计数器自加1,直至到尾节点的指针为空,此时结束循环,返回计数器*/
{
/*int num=0;
Node<T> *p = first->next;
while (p!= NULL)
{
p = p->next;
num++;
}*/
return m_Length; //添加数据成员length后,使得返回链表的长度函数更简单,代码更少
/*return num;*/
}
template<typename T>
T LinkList<T>::Get(int i) //按位查找,返回第i个节点的元素
{
if (i > m_Length) {
throw string("位置大于当前链长度"); //输入的位置有误,抛出数字1异常
}
else if(i<=0){
throw string("位置小于等于0,无效!");
}
Node<T> *p = address[i - 1];
return p->data;
/*Node<T> *p = first->next;
int count = 1;
while (p != NULL&&count < i)
{
p = p->next;
count++;
}
if (p == NULL)
{
throw"位置";
}
else
{
return p->data;
}*/
}
template<typename T>
int LinkList<T>::Locate(T x) //按值查找,返回d第一个匹配值的序号
{
Node<T> *p = first->next;
int count = 1;
while (p != NULL)
{
if (p->data == x)
{
return count;
}
p = p->next;
count++;
}
return -1; //返回-1表示没有找到
}
template<typename T>
bool LinkList<T>::Insert(int i, T x) //往链表中插入元素,i为要插入的位置,x为要插入的值
{
if (i>m_Length) {
throw string("位置大于当前链表长度!");
}
else if (i<=0) {
throw string("位置小于等于0,无效!");
}
else if (m_Length>=MaxSize) {
throw string("链满");
}
Node<T> *p = first;
int count = 0;
int num = i - 1;
while (p != NULL&&count <num)
{
p = p->next;
count++;
}
if (p == NULL)
{
return false;
}
else
{
Node<T> *s = new Node<T>;
s->data = x;
s->next = p->next;
p->next = s;
m_Length++;
for (int j = m_Length-1; j >= i; j--) { /*间接寻址数组地址的改变(增加地址)*/
address[j] = address[j - 1];
}
address[i] = s; //新地址
return true;
}
}
template<typename T>
void LinkList<T>::ListTraverse()
{
Node<T> *p = first->next;
while (p != NULL)
{
cout << p->data << ",";
p = p->next; //遍历的指针的后移,注意不能写成p++,因为这是节点
}
}
template<typename T>
bool LinkList<T>::Delete(int i)
{
if (i>m_Length||i<=0) { //相当于顺序表中的判空判满
throw string("位置无效!");
}
Node<T> *p = first;
int count = 0;
while (p != NULL&&count < i - 1)
{
p = p->next;
count++;
}
if (p == NULL)
{
return false;
}
else
{
Node<T> *q;
q = p->next;
p->next = q->next;
delete q;
m_Length--;
for (int j = i; j <= m_Length; j++) { //间接寻址地址改变
address[j-1] = address[j];
}
return true;
}
}
template<typename T>
bool LinkList<T>::changeList(int i,T x) {
if (i>m_Length||i<=0) {
throw string("位置有误");
}
Node<T> *p = address[i - 1];
p->data = x;
return true;
}
调用测试成员函数(合法):
#include<iostream>
#include"IndirectList.h"
#include<stdlib.h>
#include<string>
using namespace std;
int main() {
string a[5] = { "plus","plus","c","world","hello" };
try
{
LinkList<string> MyList(a, 5);
string L = MyList.Get(1);
cout << "第一个节点位置的元素为:" << L << endl;
cout <<"当前链长度为:"<< MyList.Length() << endl;
int Lo = MyList.Locate("hello");
if (Lo == -1) {
cout << "未找到" << endl;
}
else {
cout << "元素hello所在的位置为:" << MyList.Locate("hello") << endl;
}
MyList.ListTraverse();
cout << endl;
cout << "尾插法插入元素(插在最后)";
MyList.InsertTail("where");
MyList.ListTraverse();
cout << endl;
cout << "头插法插入元素,插在第一个";
MyList.InsertHead("areyou");
MyList.ListTraverse();
cout << endl;
cout << "插入元素到指定位置";
MyList.Insert(3, "thereismiddle");
MyList.ListTraverse();
cout << endl;
cout << "删除指定位置的元素";
MyList.Delete(3);
MyList.ListTraverse();
cout << endl;
cout << "改变指定位置元素的值";
MyList.changeList(1, "ppp"); //改变第一个节点的元素
MyList.ListTraverse();
}
catch (string& aval)
{
cout << aval << endl;
}
return 0;
}
效果截图:
调用成员函数(非法):
当MaxSize为4,传入五个元素,则初始化结果为:
调用Get(int i) ,传入的i函数非法(大于链长度或小于1)时:
更多非法数据的输入调用结果不再截图(类似,都是下标的上溢或者下溢或者链满)。
以上!