目录
命名规范
在数据结构中,我们会经常使用代码来表示链表,树,图等,那我们怎么对它们进行命名呢?
- 首先我们不能用拼音来命名,这是极大的错误
- 也不能用一些无意义的名称来表示
对于C++来说,我们一般将这些命名的和它的STL库类似即可
当我们讨论C++中数据结构的命名,并且想要遵循类似STL的命名约定时,我们可以避免使用具体的代码实现,而只是关注命名本身。
以下是一些仿照STL命名风格的常见数据结构命名示例:
- 顺序表:
SequenceList
ArrayList
DynamicArray
- 链表:
LinkedList
SinglyLinkedList
(单链表)DoublyLinkedList
(双链表)CircularLinkedList
(循环链表)
- 栈:
Stack
DynamicStack
ArrayStack
- 队列:
Queue
Deque
(双端队列)PriorityQueue
(优先队列)
- 串:
String
(虽然C++标准库已经有一个std::string
,但如果你需要自定义的字符串类,可以这样命名)CustomString
TextBuffer
- 树:
Tree
BinaryTree
AVLTree
BTree
Trie
(字典树)HuffmanTree
(哈夫曼树)
- 图:
Graph
DirectedGraph
(有向图)UndirectedGraph
(无向图)WeightedGraph
(加权图)AdjacencyMatrixGraph
(邻接矩阵表示的图)AdjacencyListGraph
(邻接表表示的图)
请注意,上述命名只是为了提供一个思路,实际的命名应根据具体的应用场景和项目的约定来决定。
此外,如果你正在编写一个库或框架,并且想要提供类似STL的功能,那么确保你的命名风格与STL保持一致是很重要的,这样可以提高代码的可读性和可维护性。
最后,命名不仅仅是选择单词,还包括使用大小写、前缀和后缀等方式来区分不同的数据类型和特性。保持一致性是命名过程中的关键原则。
顺序表的定义
顺序表分为静态顺序表和动态顺序表
静态顺序表
typedef int SLDataType;
#define N 100000
// 静态顺序表 -- 开少了不够用 开多了浪费
struct SeqList
{
SLDataType a[N];
int size;
};
静态顺序表是一种在数据结构中常见的线性表实现方式,它使用定长数组来存储数据元素。这种数据结构在初始化时就确定了存储空间的大小,并在运行期间保持不变。
以下是对静态顺序表优缺点的分析:
优点:
- 空间预先分配:静态顺序表在创建时就分配了固定的内存空间,这避免了在运行时频繁地申请或释放内存,从而减少了内存碎片的问题。
- 存取速度快:由于数据元素在内存中是连续存放的,因此可以通过下标直接计算出元素在内存中的位置,实现快速存取。
- 代码简单:静态顺序表的实现相对简单,因为数组的操作是基础的编程技能,所以理解和使用都比较容易。
缺点:
- 空间限制:静态顺序表的大小在初始化时就已确定,因此它不能动态地扩展或缩小。如果预先分配的空间过大,会造成内存浪费;如果空间过小,又无法存储足够的数据,可能出现“溢出”问题。
- 插入和删除操作复杂:在静态顺序表中插入或删除元素时,可能需要移动大量的元素以保证数据的连续性,这导致插入和删除操作的时间复杂度较高,效率较低。
- 不灵活:由于静态顺序表的大小是固定的,因此在面对不确定数据规模的情况时,使用静态顺序表可能会带来问题。当需要存取的元素个数可能多于顺序表的元素个数时,就会出现问题。
综上所述,静态顺序表在数据规模确定且不需要频繁改变的情况下表现良好,但在面对动态变化的数据规模时,其缺点就显得尤为突出。因此,在选择使用静态顺序表时,需要根据具体的应用场景和需求进行权衡。
动态顺序表
typedef int SLDataType;
#define INIT_CAPACITY 4
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据个数
int capacity; // 空间容量
}SL;
动态顺序表是一种可以动态调整其大小的顺序表,它克服了静态顺序表在大小固定方面的限制。
以下是动态顺序表的优点和缺点:
优点:
- 动态调整大小:动态顺序表能够根据数据的实际需求动态地扩展或缩小存储空间,从而避免了静态顺序表在存储空间分配上可能出现的浪费或不足的问题。
- 高效存储:由于动态顺序表在内存中连续存储数据,因此它保留了顺序表随机访问效率高的特点。通过下标可以直接访问任意位置的元素,这在处理大量数据时非常有用。
- 灵活性强:动态顺序表适用于数据规模不确定或可能频繁变化的情况。它可以随着数据的增减而自动调整大小,无需预先估计数据规模。
缺点:
- 空间开销:虽然动态顺序表能够根据需要动态调整大小,但在扩展存储空间时可能需要额外的内存分配和数据复制操作。这可能导致一定的空间和时间开销。
- 插入和删除操作的效率:尽管动态顺序表能够动态调整大小,但在插入或删除元素时,仍可能需要移动其他元素以保持数据的连续性。这尤其在元素数量较大或需要频繁进行插入/删除操作时可能导致效率降低。
- 管理复杂:动态顺序表需要维护额外的元数据(如当前大小、最大容量等),并需要实现相应的内存管理操作(如分配、释放等)。这增加了实现的复杂性和出错的可能性。
总的来说,动态顺序表在处理动态变化的数据规模时具有较大的优势,但也需要考虑其带来的额外开销和实现的复杂性。在选择使用动态顺序表时,需要根据具体的应用场景和需求进行权衡。
接下来,我们将以动态顺序表为例,来讲述顺序表的增删查改等基本操作
顺序表的基本操作
我们将顺序表的所有操作都定义在一个叫SeqList的头文件里面,其中Seq是顺序的英文单词的缩写,List表示表的意思
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
#define INIT_CAPACITY 4
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据个数
int capacity; // 空间容量
}SL;
// 增删查改
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);
这些操作的前面都带有SL的前缀,是顺序表的缩写,后面则代表它们执行的操作