数据结构与算法学习笔记——List ADT C++实现
List ADT
1 特点
List的数据结构为线性结构
线性结构的特点:各结点元素间存在顺序关系,每个元素(除了头部和尾部)都有唯一的前驱和后继
2 包含的操作
- 插入一个元素:insert()
- 删除一个元素:remove()
- 查找第k个元素:findKth()
- 顺序打印List:print()
- 反转List:invert()
3 实现方式
3.1 基于数组实现
基于数组实现的List做查找操作比较简单,但做插入和删除操作时会产生大量元素移动
做插入操作时,移动元素要从后往前;做删除操作时,移动元素要从前往后
实际上这里结点元素的总数不能超出定义数组时数组的大小,避免超出范围时再插入元素的程序没有写
List.h
#include <iostream>
const int N = 12; // 数组大小
template <typename T>
class List
{
public:
List();
~List();
T array[N] = {0}; // 数组
T findKth(int);
void insert(int, T);
void remove(int);
void invert();
void print();
private:
int _number; // 记录链表元素总数(从1开始计)
protected:
};
List.cpp
#include "List.h"
/*
* 创建链表,以-1为结束标志
*/
template <typename T>
List<T>::List()
{
_number = 0;
for(int i = 0; i < N; i++)
{
std::cout << "input the num:" << std::endl;
std::cin >> array[i];
if(array[i] == -1)
break;
_number++;
}
}
/*
* 清空List
*/
template <typename T>
List<T>::~List()
{
}
/*
* 查找第k个元素(k从0开始)
*/
template <typename T>
T List<T>::findKth(int k)
{
return array[k];
}
/*
* 在第k位置插入一个元素,插入元素的成员num值为a
*/
template <typename T>
void List<T>::insert(int k, T a)
{
for(int i = _number - 1; i >= k; i--)
{
array[i + 1] = array[i];
}
array[k] = a;
_number++;
}
/*
* 删除第k位置的元素
*/
template <typename T>
void List<T>::remove(int k)
{
for(int i = k; i < _number - 1; i++)
{
array[i] = array[i + 1];
}
_number--;
}
/*
* 将链表逆序
*/
template <typename T>
void List<T>::invert()
{
for(int i = 0; i < _number / 2; i++)
{
T temp;
temp = array[i];
array[i] = array[_number - 1 -i];
array[_number - 1 -i] = temp;
}
}
/*
* 打印链表所有元素
*/
template <typename T>
void List<T>::print()
{
for(int i = 0; i < _number; i++)
{
std::cout << array[i] << '\t';
}
std::cout << std::endl;
}
template class List<int>;
template class List<float>;
template class List<double>;
main.cpp
#include <iostream>
#include "List.h"
int main()
{
List<int> list1;
list1.print();
int n, num;
std::cout << "you want to insert to node n:" << std::endl;
std::cin >> n;
std::cout << "input the num:" << std::endl;
std::cin >> num;
list1.insert(n, num);
list1.print();
std::cout << "you want to delete node n:" << std::endl;
std::cin >> n;
list1.remove(n);
list1.print();
list1.invert();
list1.print();
std::cout << "the 6th node's data is: " << list1.findKth(6) << std::endl;
return 0;
}
运行结果:
3.2 基于单链表实现
基于单链表实现时做插入、删除操作比较简单,但查找操作比基于数组的List复杂
结点元素的创建和清除分别写在了List类构造函数和析构函数中。清空List时要注意,delete之后要给对应指针赋值为NULL,以避免出现野指针
在插入和删除操作中,要注意插入/删除的是否为表头/尾部
List.h
#include <iostream>
template <typename T> // 节点
struct Node
{
T data;
Node *next;
};
template <typename T>
class List
{
public:
List();
~List();
Node<T> *head; // 头指针
Node<T> *findKth(int);
void insert(int, T);
void remove(int);
void invert();
void print();
private:
int _number; // 记录链表元素总数(从1开始计)
protected:
};
List.cpp
#include "List.h"
/*
* 创建链表,以-1为结束标志
*/
template <typename T>
List<T>::List()
{
// 注释掉的内容是另一种创建链表的方法,仅用一个指针即可实现,缺点是无法把结束标志元素“-1”去除出链表
// Node<T> *head, *p;
// head = p = new Node<T>;
// std::cout << "input the num:" << std::endl;
// std::cin >> p->data;
// _number = 0;
// for(;p->data != -1;)
// {
// p->next = new Node<T>;
// p = p->next;
// std::cout << "input the num:" << std::endl;
// std::cin >> p->data;
// _number++;
// }
// p->next = NULL;
Node<T> *p1, *p2;
this->head = p1 = p2 = new Node<T>;
std::cout << "input the num:" << std::endl;
std::cin >> this->head->data;
_number = 1;
for(;;)
{
p1->next = new Node<T>;
p1 = p1->next;
std::cout << "input the num:" << std::endl;
std::cin >> p1->data;
if(p1->data == -1)
break;
p2 = p1;
_number++;
}
delete p2->next;
p2->next = NULL;
}
/*
* 清空List
*/
template <typename T>
List<T>::~List()
{
Node<T> *p1 = this->head;
Node<T> *p2 = p1;
for(;p1 != NULL;)
{
p1 = p1->next;
delete p2;
p2 = p1;
}
p2 = NULL; // 要记住delete后面要有 p=NULL 以避免出现野指针
}
/*
* 查找第k个元素(k从0开始)
*/
template <typename T>
Node<T> *List<T>::findKth(int k)
{
Node<T> *p = this->head;
for(int i = 0; i < k; i++)
p = p->next;
return p;
}
/*
* 在第k位置插入一个元素,插入元素的成员num值为a
*/
template <typename T>
void List<T>::insert(int k, T a)
{
Node<T> *pre = this->head;
Node<T> *p = new Node<T>;
if(k == 0)
{
this->head = p;
p->next = pre;
}
else
{
pre = findKth(k - 1);
p->next = pre->next;
pre->next = p;
}
p->data = a;
_number++;
}
/*
* 删除第k位置的元素
*/
template <typename T>
void List<T>::remove(int k)
{
Node<T> *pre = this->head;
Node<T> *p = new Node<T>;
if(k == 0)
{
delete this->head;
this->head = pre->next;
}
else
{
pre = findKth(k - 1);
p = pre->next;
pre->next = pre->next->next;
delete p;
p = NULL;
}
_number--;
}
/*
* 将链表逆序
*/
template <typename T>
void List<T>::invert()
{
Node<T> *pre, *cur, *next;
pre = this->head;
cur = this->head->next;
next = this->head->next->next;
this->head->next = NULL;
for(;next != NULL;)
{
cur->next = pre;
pre = cur;
cur = next;
next = next->next;
}
cur->next = pre;
this->head = cur;
}
/*
* 打印链表所有元素
*/
template <typename T>
void List<T>::print()
{
Node<T> *p = this->head;
for(; p != NULL; p = p->next)
std::cout << p->data << '\t';
std::cout << std::endl;
}
template class List<int>;
template class List<float>;
template class List<double>;
main.cpp
#include <iostream>
#include "List.h"
int main()
{
List<int> list1;
list1.print();
int n, num;
std::cout << "you want to insert to node n:" << std::endl;
std::cin >> n;
std::cout << "input the num:" << std::endl;
std::cin >> num;
list1.insert(n, num);
list1.print();
std::cout << "you want to delete node n:" << std::endl;
std::cin >> n;
list1.remove(n);
list1.print();
std::cout << "the inverted List is:" << std::endl;
list1.invert();
list1.print();
std::cout << "the 6th node's data is: " << list1.findKth(6)->data << std::endl;
return 0;
}
运行结果(结果同上):
3.3 基于双向链表实现
基于双向链表的List同样具有基于单链表List插入删除简单的优势,同时比单链表List更方便查找
在插入/删除操作时,也要考虑要操作的是否为表头或尾部,要注意的情况比单链表时稍多
添加了函数inv_print(),功能是逆序打印,写这个函数的目的是测试元素与其前一个元素的连接关系是否正常建立
List.h
#include <iostream>
template <typename T>
struct Node
{
T data;
Node *pre, *next;
};
template <typename T>
class List
{
public:
List();
~List();
Node<T> *head;
Node<T> *findKth(int);
void insert(int, T);
void remove(int);
void invert();
void print();
void inv_print();
private:
int _number = 0; // 记录链表元素总数(从1开始计)
protected:
};
List.cpp
#include "List.h"
/*
* 创建链表,以-1为结束标志
*/
template <typename T>
List<T>::List()
{
Node<T> *p1, *p2;
head = p1 = p2 = new Node<T>;
head->pre = NULL;
std::cout << "input the num:" << std::endl;
std::cin >> head->data;
for(;p1->data != -1;)
{
p2 = p1;
_number++;
p1->next = new Node<T>;
p1 = p1->next;
std::cout << "input the num:" << std::endl;
std::cin >> p1->data;
p1->pre = p2;
}
delete p2->next;
p2->next = NULL;
}
/*
* 清空List
*/
template <typename T>
List<T>::~List()
{
Node<T> *p1, *p2;
p1 = head;
for(;p1 != NULL;)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
p2 = NULL;
}
/*
* 查找第k个元素(k从0开始)
*/
template <typename T>
Node<T> *List<T>::findKth(int k)
{
Node<T> *p = head;
for(int i = 0; i < k; i++)
p = p->next;
return p;
}
/*
* 在第k位置插入一个元素,插入元素的成员num值为a
*/
template <typename T>
void List<T>::insert(int k, T a)
{
Node<T> *p = new Node<T>;
if(k == 0)
{
p->pre = NULL;
p->data = a;
p->next = head;
head = p;
}
else if(k == _number)
{
p->pre = findKth(k - 1);
p->data = a;
p->next = p->pre->next;
p->pre->next = p;
}
else
{
p->pre = findKth(k - 1);
p->data = a;
p->next = p->pre->next;
p->pre->next = p;
p->next->pre = p;
}
_number++;
}
/*
* 删除第k位置的元素
*/
template <typename T>
void List<T>::remove(int k)
{
Node<T> *p;
if(k == 0)
{
p = findKth(k);
head = p->next;
}
else if(k == (_number - 1))
{
p = findKth(k);
p->pre->next = NULL;
}
else
{
p = findKth(k);
p->pre->next = p->next;
p->next->pre = p->pre;
}
delete p;
p = NULL;
_number--;
}
/*
* 将链表逆序
*/
template <typename T>
void List<T>::invert()
{
Node<T> *p;
for(p = head->next; p->next != NULL; p = p->pre)
{
Node<T> *temp;
temp = p->pre;
p->pre = p->next;
p->next = temp;
}
head->pre = head->next;
head->next = NULL;
p->next = p->pre;
p->pre = NULL;
head = p;
}
/*
* 打印链表所有元素
*/
template <typename T>
void List<T>::print()
{
Node<T> *p = head;
for(;p != NULL; p = p->next)
std::cout << p->data << '\t';
std::cout << std::endl;
}
/*
* 逆序打印链表所有元素
*/
template <typename T>
void List<T>::inv_print()
{
Node<T> *p = findKth(_number - 1);
for(; p != NULL; p = p->pre)
std::cout << p->data << '\t';
std::cout << std::endl;
}
template class List<int>;
template class List<float>;
template class List<double>;
main.cpp
#include <iostream>
#include "List.h"
int main()
{
List<int> list1;
list1.print();
int n, num;
std::cout << "you want to insert to node n:" << std::endl;
std::cin >> n;
std::cout << "input the num:" << std::endl;
std::cin >> num;
list1.insert(n, num);
list1.print();
std::cout << "you want to delete node n:" << std::endl;
std::cin >> n;
list1.remove(n);
list1.print();
std::cout << "the inverted List is:" << std::endl;
list1.invert();
list1.print();
std::cout << "the List printed in reverse order is:" << std::endl;
list1.inv_print();
std::cout << "the 6th node's data is: " << list1.findKth(6)->data << std::endl;
return 0;
}
运行结果: