📅  最后修改于: 2023-12-03 15:00:33.051000             🧑  作者: Mango
数据结构与算法 (Data Structures and Algorithms, DSA) 是计算机科学中一门关于数据结构与算法的课程,同时也可以理解为一门由底层到高层的学科,它是介于算法学和计算机程序设计之间的一门富有挑战性的专题课程。
数据结构是计算机存储、组织数据的一种方式,是指相互之间存在一种或多种特定关系的数据元素的集合。常见的数据结构包括:数组,链表,栈,队列,树,图等等。
数组是一种数据类型,是有序数据元素的集合。它具有一定大小(即数组元素的个数),根据自身的下标去访问每一个元素。数组的访问时间复杂度为O(1)。
例子:
int arr[] = {1, 2, 3, 4, 5};
for(int i = 0; i < 5; i++){
printf("%d ", arr[i]);
}
输出:1 2 3 4 5
链表是一种线性数据结构,数据元素不一定是相邻的,每个元素包含两个字段:一个是存储该元素的数据,另一个是指向下一个元素的指针。链表的访问时间复杂度为O(n)。
例子:
struct ListNode {
int val;
struct ListNode *next;
};
struct ListNode* init(){
struct ListNode *head = (struct ListNode*)malloc(sizeof(struct ListNode));
head->val = 1;
head->next = NULL;
struct ListNode *p = head;
for(int i = 2; i < 6; i++){
struct ListNode *temp = (struct ListNode*)malloc(sizeof(struct ListNode));
temp->val = i;
temp->next = NULL;
p->next = temp;
p = p->next;
}
return head;
}
void print(struct ListNode *head){
struct ListNode *p = head;
while(p){
printf("%d ", p->val);
p = p->next;
}
printf("\n");
}
输出:1 2 3 4 5
栈是一种后进先出 (Last In First Out, LIFO) 的线性数据结构,具有压入和弹出两种基本操作,栈顶元素是最近被压入的元素。栈的访问时间复杂度为O(1)。
例子:
const int N = 100010;
int stk[N], tt = -1;
void push(int x){
stk[++tt] = x;
}
void pop(){
tt--;
}
int top(){
return stk[tt];
}
bool empty(){
return tt == -1;
}
输出:
队列是一种先进先出 (First In First Out, FIFO) 的线性数据结构,具有入队和出队两种基本操作,队头元素是最先被插入的元素。队列的访问时间复杂度为O(1)。
例子:
const int N = 100010;
int q[N], hh = 0, tt = -1;
void push(int x){
q[++tt] = x;
}
void pop(){
hh++;
}
int front(){
return q[hh];
}
bool empty(){
return hh <= tt;
}
输出:
树是一种非线性数据结构,由节点和边组成。节点具有父节点和子节点,根节点没有父节点,叶子节点没有子节点。树分为二叉树、平衡树、哈夫曼树等多种类型。
例子:
struct TreeNode{
int val;
struct TreeNode *left, *right;
};
struct TreeNode* build(){
struct TreeNode *root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = 1;
root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->left->val = 2;
root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->val = 3;
root->left->left = NULL;
root->left->right = NULL;
root->right->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->left->val = 4;
root->right->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->right->val = 5;
root->right->left->left = NULL;
root->right->left->right = NULL;
root->right->right->left = NULL;
root->right->right->right = NULL;
return root;
}
void preorder(struct TreeNode *root){
if(root == NULL) return;
printf("%d ", root->val);
preorder(root->left);
preorder(root->right);
}
void inorder(struct TreeNode *root){
if(root == NULL) return;
inorder(root->left);
printf("%d ", root->val);
inorder(root->right);
}
void postorder(struct TreeNode *root){
if(root == NULL) return;
postorder(root->left);
postorder(root->right);
printf("%d ", root->val);
}
输出:
preorder: 1 2 3 4 5
inorder: 2 1 4 3 5
postorder: 2 4 5 3 1
算法是指如何解决特定问题的一系列清晰的指令和规则,它是解决问题的准确而完整的描述。经典算法有:排序、查找、图论算法、动态规划算法等等。
排序是将一串记录按照一定的顺序进行排列的过程。常见排序算法有:快速排序、归并排序、冒泡排序、插入排序、选择排序、堆排序等等。
例子:
void quicksort(int q[], int l, int r){
if(l >= r) return;
int x = q[l + r >> 1], i = l - 1, j = r + 1;
while(i < j){
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
quicksort(q, l, j);
quicksort(q, j + 1, r);
}
void mergesort(int q[], int l, int r){
if(l == r) return;
int mid = l + r >> 1;
mergesort(q, l, mid), mergesort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
int tmp[r - l + 1];
while(i <= mid && j <= r) tmp[k++] = q[i] <= q[j] ? q[i++] : q[j++];
while(i <= mid) tmp[k++] = q[i++];
while(j <= r) tmp[k++] = q[j++];
for(int i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
void bubblesort(int a[], int n){
for(int i = 0; i < n - 1; i++){
for(int j = 0; j < n - 1 - i; j++){
if(a[j] > a[j + 1]){
swap(a[j], a[j + 1]);
}
}
}
}
void insertsort(int a[], int n){
for(int i = 1; i < n; i++){
int j = i - 1, t = a[i];
while(j >= 0 && a[j] > t){
a[j + 1] = a[j];
j--;
}
a[j + 1] = t;
}
}
void selectsort(int a[], int n){
for(int i = 0; i < n - 1; i++){
int k = i;
for(int j = i + 1; j < n; j++){
if(a[j] < a[k]){
k = j;
}
}
swap(a[i], a[k]);
}
}
void heapsort(int a[], int n){
priority_queue<int, vector<int>, greater<int>> q;
for(int i = 0; i < n; i++){
q.push(a[i]);
}
for(int i = 0; i < n; i++){
a[i] = q.top();
q.pop();
}
}
输出:
查找是在数据集合中按照一定的规则,找出满足条件的数据元素。常见查找算法有:顺序查找、二分查找、哈希查找等等。
例子:
int bsearch(int q[], int n, int x){
int l = 0, r = n - 1;
while(l <= r){
int mid = l + r >> 1;
if(q[mid] == x) return mid;
else if(q[mid] < x) l = mid + 1;
else r = mid - 1;
}
return -1;
}
输出:
图是一种由点和边组成的数据结构,图的应用非常广泛。常见的图论算法有:最短路算法、最小生成树算法、拓扑排序算法等等。
例子:
struct Edge{
int v, w;
};
vector<Edge> g[N];
void dijkstra(int s, int dis[]){
memset(dis, 0x3f, sizeof(dis));
dis[s] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
q.push({0, s});
while(q.size()){
auto t = q.top(); q.pop();
int d = t.first, x = t.second;
if(dis[x] != d) continue;
for(auto e: g[x]){
if(dis[e.v] > dis[x] + e.w){
dis[e.v] = dis[x] + e.w;
q.push({dis[e.v], e.v});
}
}
}
}
输出:
动态规划算法是一种将原问题分解为多个子问题进行求解的方法,将子问题的解缓存起来,避免重复计算。常见的动态规划算法有:背包问题、最长公共子序列、最大子段和等等。
例子:
int f[N], a[N];
int n;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n ;i++){
scanf("%d", &a[i]);
}
int ans = -1e9;
for(int i = 1; i <= n ;i++){
f[i] = max(f[i - 1] + a[i], a[i]);
ans = max(ans, f[i]);
}
printf("%d\n", ans);
return 0;
}
输出:
实现一个完美的软件需要有一定的算法和数据结构知识作为依托。DSA 的学习对于开发者来说是非常必要的,它可以帮助我们解决各种算法和数据结构问题,并将其应用到实际的生产中。熟练掌握 DSA 这一门常用的技能,能够提高程序员的工作效率,为程序员的职业生涯带来更多的机会。