一、数据结构的概念
数据结构是计算机科学中一种重要的基础概念,它是指数据对象及其之间的关系,是计算机存储、组织数据的方式。数据结构既包含数据对象的物理结构,也包括它们之间的逻辑联系和操作。通俗地说,数据结构是为解决某一类问题,而组织起来的特定方式的数据类型的总称。
数据结构包括线性结构、树形结构、图形结构等多种类型,每一种类型都对应着不同的应用领域。比如,线性结构可以用来表示递进关系,树形结构可以用来表示层级关系,图形结构可以用来表示各种关系。其中,线性结构最为简单,也最常用,因此下面将以线性结构为例子来对数据结构进行详细阐述。
二、线性结构
线性结构是指数据元素之间存在一个唯一的前驱后继关系的结构。常见的线性结构有数组、链表、队列和栈等。下面将以链表为例子来详细介绍。
1. 链表的定义
<template<typename T> class LinkedList {
private:
struct Node {
T data;
Node* next;
Node(const T& data=T(), Node* next=nullptr) : data(data), next(next) {}
};
int size;
Node* dummyHead;
}
以上是链表的定义代码。链表是由多个结点构成的,每个结点都有一个数据域和一个指向下一个结点的指针域。链表的第一个结点没有前驱,最后一个结点没有后继,所以需要在链表的头部添加一个虚拟结点(dummyHead)来便于操作。
2. 链表的操作
链表的操作包括添加、删除、查找等,下面将以添加操作为例子来介绍。链表的添加操作有两种,分别是在链表头部添加结点和在链表尾部添加结点。
(1) 在链表头部添加结点
void addFirst(const T& data) {
dummyHead->next = new Node(data, dummyHead->next);
size++;
}
在链表的头部添加一个结点,只需要将新结点的指针域指向原来的头结点,再将dummyHead的指针域指向新的头结点即可。
(2) 在链表尾部添加结点
void addLast(const T& data) {
Node* cur = dummyHead;
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = new Node(data);
size++;
}
在链表的尾部添加一个结点,只需要先找到链表的最后一个结点,再将该结点的指针域指向新的结点即可。
三、其他数据结构
除了线性结构,数据结构还包括树形结构、图形结构等多种类型。树形结构包括二叉树、平衡树、B树等,图形结构包括有向图、无向图等。它们的定义和操作都有所不同,但是都有着其独特的特点和应用场景。
1. 二叉搜索树
二叉搜索树是一种特殊的二叉树,它的每个结点都有一个关键字(Key),并且所有左子树结点的关键字都小于该结点的关键字,所有右子树结点的关键字都大于该结点的关键字。下面是二叉搜索树的定义和一些操作代码。
<template<typename T> class BST {
private:
struct Node {
T data;
Node* left;
Node* right;
Node(const T& data=T(), Node* left=nullptr, Node* right=nullptr) : data(data), left(left), right(right) {}
};
int size;
Node* root;
...
public:
// 向二分搜索树中添加元素e
void add(const T& e) {
root = add(root, e);
}
private:
Node* add(Node* node, const T& e) {
if (node == nullptr) {
size++;
return new Node(e);
}
if (e < node->data) {
node->left = add(node->left, e);
} else if (e > node->data) {
node->right = add(node->right, e);
}
return node;
}
}
以上是二叉搜索树的定义和添加操作代码。二叉搜索树的定义较为简单,只需要保证左子树比该结点小,右子树比该结点大即可。在添加元素时,只需要比较该元素与当前结点的大小关系即可。如果比当前结点小,就继续在左子树中添加; 如果比当前结点大,就继续在右子树中添加。
2. 图形结构
图形结构有向图、无向图等多种类型。有向图中每个结点有多个入度和多个出度,无向图中每个结点都有多个度数。以下是一个无向图的定义和遍历操作的代码。
<template<typename T> class Graph {
private:
vector<vector<T>> adj;// 图的邻接表
vector<bool> visited; // 标记每个结点是否被遍历过
...
public:
// 深度优先搜索算法
void dfs(int v) {
visited[v] = true;
cout << v << " ";
for (T w : adj[v]) {
if (!visited[w]) {
dfs(w);
}
}
}
}
以上是无向图的定义和深度优先遍历算法。无向图中每个结点都有多个度数,因此需要使用邻接表来表示。深度优先遍历算法需要从一个指定的结点开始遍历,将该结点标记为已遍历,然后递归遍历其邻接结点。深度优先遍历算法可以用来解决一些搜索问题,比如迷宫问题。