1. 思维导图
一张简单的思维导图有助于你理解顺序表的一些特性,下面是一张简单的顺序表思维导图:
2. 代码实现
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
#define InitSize 5
template<typename T>
struct SeqList {
T* data;
int length;
int maxSize;
};
struct Person {
string name;
int age;
// << 运算符重载
friend ostream& operator<<(ostream& output, const Person& p) {
output << "Name: " << p.name << " Age: " << p.age << endl;
return output;
}
// == 运算符重载
bool operator==(const Person& p) const {
return (name == p.name && age == p.age);
}
};
template<typename T>
void printValue(T value) {
cout << value << " ";
}
template<typename T>
class Test {
public:
SeqList<T> seqList;
// 无参构造
Test() {
InitList();
}
// 有参构造
Test(T value) {
InitList();
this->seqList.data[0] = value;
this->seqList.length++;
}
// 拷贝构造
Test(const Test<T>& t) {
this->seqList.maxSize = t.seqList.maxSize;
this->seqList.data = new T[this->seqList.maxSize];
this->seqList.length = t.seqList.length;
for (int i = 0; i < this->seqList.length; i++)
this->seqList.data[i] = t.seqList.data[i];
}
// 初始化(静态分配)
void InitList() {
this->seqList.maxSize = InitSize;
this->seqList.data = new T[this->seqList.maxSize];
this->seqList.length = 0;
}
// 动态增加顺序表长度(动态分配)
void UpdateList(int increment) {
// 保存原有的数据
T* temp = this->seqList.data;
// 新分配内存,并拷贝原有数据
this->seqList.data = new T[this->seqList.maxSize + increment];
for (int i = 0; i < this->seqList.length; i++)
this->seqList.data[i] = temp[i];
// 更新数据容量
this->seqList.maxSize += increment;
// 释放原有数据占用的内存
delete[] temp;
}
// 头补数据
void PushHead(T value) {
if (this->seqList.length == this->seqList.maxSize)
UpdateList(1);
for (int i = this->seqList.length; i > 0; i--)
this->seqList.data[i] = this->seqList.data[i - 1];
this->seqList.data[0] = value;
this->seqList.length++;
}
// 头删数据
void PopHead() {
for (int i = 0; i < this->seqList.length - 1; i++)
this->seqList.data[i] = this->seqList.data[i + 1];
this->seqList.length--;
}
// 尾补数据
void PushBack(T value) {
if (this->seqList.length == this->seqList.maxSize)
UpdateList(1);
this->seqList.data[this->seqList.length] = value;
this->seqList.length++;
}
// 尾删数据
void PopBack() {
this->seqList.length--;
}
// 在位序为 rank 的位置插入元素
bool Insert(int rank, T element) {
if (rank <1 || rank > this->seqList.length) {
cout << "Error:Illegal insertion position!" << endl;
return false;
}
if (this->seqList.length == this->seqList.maxSize)
UpdateList(1);
for (int i = this->seqList.length; i >= rank; i--)
this->seqList.data[i] = this->seqList.data[i - 1];
this->seqList.data[rank - 1] = element;
this->seqList.length++;
return true;
}
// 按位删除元素
bool DeleteByRank(int rank) {
if (rank <1 || rank > this->seqList.length) {
cout << "Error:Illegal deletion location!" << endl;
return false;
}
for (int i = rank - 1; i < this->seqList.length - 1; i++)
this->seqList.data[i] = this->seqList.data[i + 1];
this->seqList.length--;
return true;
}
// 根据值删除元素(所有)
void DeleteByValue(T value) {
vector<int>* rank = this->SearchByValue(value);
sort(rank->begin(), rank->end(), greater<int>());
for_each(rank->begin(), rank->end(),
std::bind(&Test::DeleteByRank, this, std::placeholders::_1));
delete rank;
}
// 按值查找
vector<int>* SearchByValue(T value) {
vector<int> rank;
for (int i = 0; i < this->seqList.length; i++)
if (this->seqList.data[i] == value)
rank.push_back(i + 1);
if (rank.empty())
cout << "Error:no value in sequence table!" << endl;
vector<int>* vec = new vector<int>(rank);
return vec;
}
// 按位修改
bool UpdateByRank(int rank, T value) {
if (rank <1 || rank > this->seqList.length) {
cout << "Error:Illegal deletion location!" << endl;
return false;
}
this->seqList.data[rank - 1] = value;
return true;
}
// 按值修改
void UpdateByValue(T originalValue, T newValue) {
for (int i = 0; i < this->seqList.length; i++)
if (this->seqList.data[i] == originalValue)
this->seqList.data[i] = newValue;
}
// 清除顺序表
void Clear() {
this->seqList.length = 0;
}
// 打印
void PrintList() {
cout << "顺序表数据为:\n ";
for (int i = 0; i < this->seqList.length; i++)
cout << this->seqList.data[i] << " ";
cout << endl;
}
};
// 已有数据类型测试代码
void test01() {
// First example
cout << "t:" << endl;
Test<int> t;
// 头尾补、头尾删
t.PushBack(56);
t.PushBack(23);
t.PushBack(56);
t.PushBack(211);
t.PushBack(56);
t.PushBack(101);
t.PushHead(110);
t.PushHead(985);
t.PushHead(13);
t.PushHead(25);
t.PushHead(777);
t.PopBack();
t.PopHead();
t.PrintList();
// 按值查找
vector<int>* SBV = t.SearchByValue(56);
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
// 按位插入
t.Insert(1, 1024);
t.PrintList();
// 按位删除
t.DeleteByRank(2);
t.PrintList();
// 按位修改
t.UpdateByRank(2, 13 * 13);
t.PrintList();
// 按值修改
t.UpdateByValue(56, 88);
t.PrintList();
// 按值查找
SBV = t.SearchByValue(56);
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
SBV = t.SearchByValue(88);
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
// 按值删除
t.DeleteByValue(88);
t.PrintList();
delete SBV;
// Second example:拷贝构造
cout << "t2:" << endl;
Test<int> t2(t);
t2.PrintList();
// Third example:有参构造
cout << "t3:" << endl;
Test<int> t3(101);
t3.PrintList();
}
// 自定义数据类型测试代码
void test02() {
// First example
cout << "l1:" << endl;
Test<Person> l1;
// 头尾补、头尾删
l1.PushHead({ "Muller", 33 });
l1.PushBack({ "Messi", 35 });
l1.PushBack({ "Tony Kroos", 32 });
l1.PushBack({ "Muller", 33 });
l1.PushBack({ "Sane", 26 });
l1.PushBack({ "Gavi", 19 });
l1.PushBack({ "Muller", 33 });
l1.PushBack({ "Neymar", 31 });
l1.PrintList();
l1.PopBack();
l1.PopHead();
l1.PrintList();
// 按值查找
vector<int>* SBV = l1.SearchByValue({ "Muller", 33 });
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
// 按位插入
l1.Insert(1, { "Ronaldo", 37 });
l1.PrintList();
// 按位删除
l1.DeleteByRank(2);
l1.PrintList();
// 按位修改
l1.UpdateByRank(2, { "Lewandowski", 34 });
l1.PrintList();
// 按值修改
l1.UpdateByValue({ "Muller", 33 }, { "T.Muller", 33 });
l1.PrintList();
// 按值查找
SBV = l1.SearchByValue({ "Muller", 33 });
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
SBV = l1.SearchByValue({ "T.Muller", 33 });
for_each(SBV->begin(), SBV->end(), printValue<int>);
cout << endl;
// 按值删除
l1.DeleteByValue({ "T.Muller", 33 });
l1.PrintList();
delete SBV;
// Second example:拷贝构造
cout << "t2:" << endl;
Test<Person> t2(l1);
t2.PrintList();
// Third example:有参构造
cout << "t3:" << endl;
Test<Person> t3({ "Muller", 33 });
t3.PrintList();
}
int main() {
//test01();
test02();
return 0;
}
3. 代码注意点
1.DeleteByValue
函数
Q:如何在类的一个成员函数中使用for_each
算法调用另一个成员函数?
A:可以使用lambda表达式或者std::bind将另一个成员函数绑定到一个函数对象上,然后把这个函数对象作为参数传递给for_each算法。举个例子,假设我们有一个类Foo,其中有两个成员函数f1和f2:
class Foo {
public:
void f1(int x) {
// do something
}
void f2() {
std::vector<int> v = {1, 2, 3, 4};
std::for_each(v.begin(), v.end(),
[=](int x){ f1(x); }); // 使用lambda表达式
// 或者
std::for_each(v.begin(), v.end(),
std::bind(&Foo::f1, this, std::placeholders::_1)); // 使用std::bind
}
};
在f2函数中,我们可以使用lambda表达式或者std::bind将f1函数绑定到函数对象上,并将这个函数对象作为参数传递给for_each算法,从而实现在f2函数中调用f1函数。注意,在lambda表达式或者std::bind中,必须将当前对象的指针通过this传递进去,以确保能够调用类的成员函数。
2.关于 const
-
const 修饰成员函数:声明中将其标记为 const,以确保它不会更改任何成员变量的值
-
const 修饰形参:可以有效地防止函数在处理参数时修改它们的值
-
const 关键字通常用于修饰类和类成员函数,用于指定变量或函数在程序执行期间不会更改
-
对于全局函数,因为它们不属于类的一部分,也没有成员变量可以被限制为只读,在全局函数中使用 const 关键字是无效的
-
全局常量可以使用 const 关键字来限定它们的值,例如:
const int MAX_SIZE = 100;
3. 动态分配内存、释放内存
void UpdateList(int increment) {
// 保存原有的数据
T* temp = this->seqList.data;
// 新分配内存,并拷贝原有数据
this->seqList.data = new T[this->seqList.maxSize + increment];
for (int i = 0; i < this->seqList.length; i++)
this->seqList.data[i] = temp[i];
// 更新数据容量
this->seqList.maxSize += increment;
// 释放原有数据占用的内存
delete[] temp;
}
4.结构体运算符重载(待解答)
结构体运算符重载的代码中有一些目前还未考虑清楚的问题,现将相关代码呈现,希望水平更高的朋友解答一下!
struct Person {
string name;
int age;
// << 运算符重载
friend ostream& operator<<(ostream& output, const Person& p) {
output << "Name: " << p.name << " Age: " << p.age << endl;
return output;
}
// == 运算符重载
bool operator==(const Person& p) const {
return (name == p.name && age == p.age);
}
};
第一篇博客,也算是迈出第一步,如有错误或者更好的思路,希望广大读者和朋友们指正,谢谢!