線性表的間接尋址方法與單鏈表十分相似,同時也與順序表相似,間接尋址分別繼承了他們的優缺點。
實現方法,在單鏈表的基礎上增加一組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)時:
更多非法數據的輸入調用結果不再截圖(類似,都是下標的上溢或者下溢或者鏈滿)。
以上!