数据结构与算法入门

数据结构简介

一 线性数据结构

1.数组(Array)

int array[5];

array[0] = 1;
array[1] = 3;

//
int array[] = {1, 3, 50};

2.链表(Linked List)

struct ListNode {
	int val;	//节点值
	ListNode *next;	//节点指针,指向下一个值
	ListNode(int x) : val(x), next(NULL) {} //构造函数,初始化列表
};


//实例化
//实例化节点
ListNode *n1 = new ListNode(4);
ListNode *n2 = new ListNode(5);
ListNode *n3 = new ListNode(1);
//创建next指针
n1->next = n2;
n2->next = n3;

class ListNode:
	def _init_(self, x):
    	self.val = x
        self.next = None;
        
# 实例化
#实例化节点
n1 = ListNode(4)
n2 = ListNode(5)
n3 = ListNode(1)
#实例化next指针
n1.next = n2
n2.next = n3

3.栈(Stack)

stack<int> stk;

//入栈出栈
stk.push(2);//元素 2 入栈
stk.push(5);//元素 5 入栈
stk.pop();//元素 5 出栈 
stk.pop();//元素 2 出栈
stack = [] #python把列表当栈用

#入栈出栈
stack.append(3)
stack.append(5)
stack.pop()
stack.pop()

4.队列(Queue)

queue<int> que;

//入队出队
que.push(1);
que.push(2);
que.pop();
que.pop();

#python通常使用双端队列 collections.deque
from collections import deque

que = deque()
#入队出队
que.append(1)
que.append(2)
que.popleft()
que.popleft()

二 非线性数据结构

1.树(Tree)

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
    
// 初始化节点
TreeNode *n1 = new TreeNode(3); // 根节点 root
TreeNode *n2 = new TreeNode(4);
TreeNode *n3 = new TreeNode(5);
TreeNode *n4 = new TreeNode(1);
TreeNode *n5 = new TreeNode(2);

// 构建引用指向
n1->left = n2;
n1->right = n3;
n2->left = n4;
n2->right = n5;
}
class TreeNode:
    def _init_(self, x):
        self.val = x		# 节点值
        self.left = None	#左子节点
        self.right = None	#右子节点
        
#初始化节点
n1 = TreeNode(3)
n2 = TreeNode(4)
n3 = TreeNode(5)
n4 = TreeNode(1)
n5 = TreeNode(2)

#节点引用指向
n1.left = n2
n1.right = n3
n2.left = n4
n2.right = n5

2.堆(Heap)

​ 堆是一种基于「完全二叉树」的数据结构,可使用数组实现。以堆为原理的排序算法称为「堆排序」,基于堆实现的数据结构为「优先队列」。堆分为「大顶堆」和「小顶堆」,大(小)顶堆:任意节点的值不大于(小于)其父节点的值。

​ **完全二叉树:**在完全二叉树中,除了最后一层可能不是满的,其他每一层从左到右都是满的。如果最后一层存在节点,则这些节点从左到右排列。完全二叉树可以用数组来表示,节点编号从上到下、从左到右依次增加,父节点和子节点之间的关系可以通过数组的索引计算得到。

​ 如下图所示,为包含 1, 4, 2, 6, 8 元素的小顶堆。将堆(完全二叉树)中的结点按层编号,即可映射到右边的数组存储形式。


通过使用「优先队列」的「压入 push()」和「弹出 pop()」操作,即可完成堆排序,实现代码如下:

//初始化最小堆
priority_queue<int, vector<int>, greater<int>> heap;

//往堆中添加元素
heap.push(1);
heap.push(4);
heap.push(2);
heap.push(6);
heap.push(8);

//出堆,每次pop出根节点
heap.pop(); //1
heap.pop(); //2
heap.pop(); //4
heap.pop(); //6
heap.pop(); //8

3.哈希表(Hashing)

​ 暂略

4.图(Graph)

​ 图是一种非线性数据结构,由「节点(顶点)vertex」和「边 edge」组成,每条边连接一对顶点。根据边的方向有无,图可分为「有向图」和「无向图」。


表示图的方法有两种:

邻接表 适合存储稀疏图(顶点较多、边较少); 邻接矩阵 适合存储稠密图(顶点较少、边较多)。

1.邻接矩阵

int vertices[5] = {1, 2, 3, 4, 5};
int edges[5][5] = {{0, 1, 1, 1, 1},
                   {1, 0, 0, 1, 0},
                   {1, 0, 0, 0, 1},
                   {1, 1, 0, 0, 1},
                   {1, 0, 1, 1, 0}};
vertices = [1, 2, 3, 4, 5];
edges = [[0, 1, 1, 1, 1],
                   [1, 0, 0, 1, 0],
                   [1, 0, 0, 0, 1],
                   [1, 1, 0, 0, 1],
                   [1, 0, 1, 1, 0]];

2.邻接表

int vertices[5] = {1, 2, 3, 4, 5};
vector<vector<int>> edges;

vector<int> edge_1 = {1, 2, 3, 4};
vector<int> edge_2 = {0, 3};
vector<int> edge_3 = {0, 4};
vector<int> edge_4 = {0, 1, 4};
vector<int> edge_5 = {0, 2, 3};

edges.push_back(edge_1);
edges.push_back(edge_2);
edges.push_back(edge_3);
edges.push_back(edge_4);
edges.push_back(edge_5);
vertices = [1, 2, 3, 4, 5]
edges = [[1, 2, 3, 4],
         [0, 3],
         [0, 4],
         [0. 1. 4],
         [0, 2, 3]]
#Python真的好简洁

算法(Algorithm)

一 排序算法

1.冒泡排序

冒泡排序是最基础的排序算法,由于其直观性,经常作为首个介绍的排序算法。其原理为:

  • 内循环: 使用相邻双指针 j , j + 1 从左至右遍历,依次比较相邻元素大小,若左元素大于右元素则将它们交换;遍历完成时,最大元素会被交换至数组最右边 。
  • 外循环: 不断重复「内循环」,每轮将当前最大元素交换至 剩余未排序数组最右边 ,直至所有元素都被交换至正确位置时结束。
void bubbleSort(vector<int> &nums) {
    int N = nums.size();
    for (int i = 0; i < N - 1; i++) {          // 外循环
        for (int j = 0; j < N - i - 1; j++) {  // 内循环
            if (nums[j] > nums[j + 1]) {
                // 交换 nums[j], nums[j + 1]
                swap(nums[j], nums[j + 1]);
            }
        }
    }
}

​ 通过增加一个**标志位 flag **,若在某轮「内循环」中未执行任何交换操作,则说明数组已经完成排序,直接返回结果即可。

void bubbleSort(vector<int> &nums) {
    int N = nums.size();
    for (int i = 0; i < N - 1; i++) {
        bool flag = false;   // 初始化标志位
        for (int j = 0; j < N - i - 1; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
                flag = true; // 记录交换元素
            }
        }
        if (!flag) break;    // 内循环未交换任何元素,则跳出
    }
}

2.快速排序

快速排序
快速排序算法有两个核心点,分别为哨兵划分 递归

​ **哨兵划分:**以数组某个元素(一般选取首元素)为 基准数 ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。

void quick_sort(int q[], int l, int r) {
    if(l >= r) return;
    
    int i = l-1, j = r+1, x = q[l+r >> 1];//哨兵和基准的选择
    while(i < j) {
        do i ++; while(q[i] < x);
        do j --; while(q[i] > x);
        if(i < j) swap(q[i], q[j]);
    }
    quick_sort(q,l,j), quick_sort(q, j+1, r);
}

3.归并排序

归并排序体现了“分而治之” 的算法思想,具体为:

  • 「分」: 不断将数组从 中点位置 划分开,将原数组的排序问题转化为子数组的排序问题;

  • 「治」: 划分到子数组长度为 1 时,开始向上合并,不断将 左右两个较短排序数组 合并为 一个较长排序数组,直至合并至原数组时完成排序;

void mergeSort(vector<int>& nums, int l, int r) {
    // 终止条件
    if (l >= r) return;
    // 递归划分
    int m = (l + r) / 2;
    mergeSort(nums, l, m);
    mergeSort(nums, m + 1, r);
    // 合并阶段
    int tmp[r - l + 1];             // 暂存需合并区间元素
    for (int k = l; k <= r; k++)
        tmp[k - l] = nums[k];
    int i = 0, j = m - l + 1;       // 两指针分别指向左/右子数组的首个元素
    for (int k = l; k <= r; k++) {  // 遍历合并左/右子数组
        if (i == m - l + 1)
            nums[k] = tmp[j++];
        else if (j == r - l + 1 || tmp[i] <= tmp[j])
            nums[k] = tmp[i++];
        else {
            nums[k] = tmp[j++];
        }
    }
}

// 调用
vector<int> nums = { 4, 1, 3, 2, 5, 1 };
mergeSort(nums, 0, nums.size() - 1);

void merge_sort(int q[], int l, int r)
{
    if(l >= r) return;
    
    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
    
    int k = 0, i = l, j = mid + 1;
    while(i <= mid && j <= r)
        if(q[i] <= q[j]) tmp[k++] = q[i++];
        else tmp[k++] = q[j++];
    
    while(i <= mid) tmp[k++] = q[i++];
    while(j <= r) tmp[k++] = q[j++];
    
    for(i = l, j = 0; i <= r; i ++, j++) q[i] = tmp[j];
}

4.其他排序方法(略

排序简介 - OI Wiki (oi-wiki.org)

附刷题网站:

题库 - 力扣 (LeetCode) 全球极客挚爱的技术成长平台

讨论区 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题库 - AcWing

后续会更新补充进阶篇

如果有什么疑问,联系qq1754171287