链表:概念,实现,《数据结构》这里实现是基于模板的
C++语言基础,指针,引用。模板。《C++Primer》有些进阶用法放在语言学习的目录
LeetCode应用,会更新在LeetCode150,目前这个系列先暂停,数据结构更新一期,再更对应的题目解题。
新手村入门,可以根据下面实现的过程中,忘记的知识点,再回头看。
新手村入门
链表概念
列表:逻辑上有序,物理上无序(动态存储策略)【与向量一样,列表也是由具有线性逻辑次序的一组元素的集合。链表结构一般化推广】
好处:降低动态操作成本(插入,删除)O(1)
劣势:静态操作(查找,需要扫描一遍列表元素)O(n)
元素称为节点;
模板函数的创建与模板类的创建
C++中的模板是一种允许我们为类或者函数定义一种通用模式的机制,这种通用模式可以用于创建多种数据类型的类或函数实例,而无需为每个类型都编写单独的代码。模板有两种主要类型:函数模板和类模板。
函数模板入门
函数模板允许我们定义一个通用的函数,该函数可以接受多种数据类型的参数。模板参数列表在函数定义之前用尖括号<>
括起来,并紧跟在template
关键字之后。
下面是一个简单的函数模板示例,它用于交换两个值的位置:
template <typename T> // 模板参数列表,typename T 是一个类型模板参数
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
在这个例子中,T
是一个类型模板参数,它在编译时会被实际类型(如int
、double
、std::string
等)替换。
类模板
类模板允许我们定义一个通用的类,该类可以用于多种数据类型。与函数模板类似,类模板的模板参数列表也位于类声明之前,并紧跟在template
关键字之后。
下面是一个简单的类模板示例,它用于创建一个动态数组:
template <typename T> // 模板参数列表
class Array {
private:
T* data;
size_t size;
public:
Array(size_t s) : size(s), data(new T[s]) {}
~Array() { delete[] data; }
T& operator[](size_t index) {
return data[index];
}
// 其他成员函数...
};
在这个例子中,T
是一个类型模板参数,它决定了数组data
中元素的类型。
模板参数列表
模板参数列表可以包含多个类型模板参数,也可以包含非类型模板参数(如整数常量或指针)。类型模板参数用typename
或class
关键字声明(两者在模板参数声明中是等价的),而非类型模板参数可以是整数、指针或其他非类型值。
下面是一个包含多个类型模板参数的类模板示例:
template <typename Key, typename Value> // 包含两个类型模板参数
class Map {
// ... 类的实现,可能包含以Key和Value为类型的成员 ...
};
下面是一个包含非类型模板参数的函数模板示例:
template <typename T, size_t N> // 包含一个类型模板参数和一个非类型模板参数
void printArray(const T (&array)[N]) {
for (size_t i = 0; i < N; ++i) {
std::cout << array[i] << ' ';
}
std::cout << std::endl;
}
在这个例子中,T
是一个类型模板参数,而N
是一个非类型模板参数(一个size_t
类型的常量)。
OOP封装
在面向对象编程(OOP)中,封装(Encapsulation)是一个核心概念,它指的是将对象的属性和方法(或称为成员变量和成员函数)隐藏在对象内部,只通过公共接口(通常是公有方法)与外部世界进行交互。封装有助于隐藏对象的内部细节,防止外部代码直接访问或修改对象的内部状态,从而确保对象的完整性和安全性。
未封装处理通常指的是在编程中没有使用封装的概念,对象的属性和方法都是公开的,可以直接被外部代码访问和修改。这种编程方式可能会导致代码的可读性、可维护性和安全性降低。
下面是一个简单的例子来说明封装处理和未封装处理的区别:
未封装处理(不推荐)
class Person {
public:
int age; // 年龄是公开的
// 没有提供任何方法来修改年龄
};
int main() {
Person p;
p.age = -5; // 外部代码可以直接修改年龄,甚至设置为一个不合理的值
return 0;
}
在这个例子中,Person
类的 age
属性是公开的,任何外部代码都可以直接访问和修改它。这可能导致问题,因为年龄不应该是一个负数。
封装处理(推荐)
class Person {
private: // 使用私有访问修饰符来隐藏属性
int age;
public:
// 提供公有方法来设置年龄
void setAge(int newAge) {
if (newAge >= 0) { // 添加一个检查来确保年龄是非负的
age = newAge;
} else {
std::cerr << "Age cannot be negative!" << std::endl;
}
}
// 提供公有方法来获取年龄
int getAge() const {
return age;
}
};
int main() {
Person p;
p.setAge(-5); // 尝试设置一个不合理的年龄
// 输出:Age cannot be negative!
// 由于设置了检查,年龄没有被修改为-5
std::cout << "Person's age: " << p.getAge() << std::endl; // 输出:Person's age: 0(或之前的值,如果之前有设置过)
return 0;
}
在这个封装处理的例子中,Person
类的 age
属性是私有的,外部代码不能直接访问或修改它。相反,我们提供了两个公有方法 setAge
和 getAge
来分别设置和获取年龄。在 setAge
方法中,我们添加了一个检查来确保年龄是非负的。这样,即使外部代码尝试设置一个不合理的年龄,它也会被拒绝,从而确保了对象的完整性和安全性。
函数模板进阶用法和注意(另一篇总结中)
指针和引用的区别 (另一篇总结中)
《数据结构(C++语言版)》不懂的专业术语
秩,线性代数的一个概念,极大线性无关。。。还在上大学的别学我,我的线代还给老师了。
专业解释,百度或者书本,在下面的链表中,简单理解为表示一个位置,像数组下标,秩相同(下标相同)。
ADT 抽象数据类型,一般指数据的某种抽象和操作
API 应用程序结构,软件之间的交互
我们下面的目标是,根据书中提供的 ADT,代码模板,写出一个可以运行的模板链表类,Let's Go!
链表之旅
第一个任务,列表节点
注意:默认链表中每个节点都有数据,并且未对数据进行封装处理。