数据结构笔记


数据结构

第一章 绪论

算法复杂度

T ( n ) = { 1 a × T ( n b ) + c × n k T(n)=\left\{\begin{aligned} &1\\ &a\times T(\cfrac nb)+c \times n^k \end{aligned}\right. T(n)=1a×T(bn)+c×nk

⇓ \Downarrow

T ( n ) = { n log ⁡ b ( a ) , a > b k n k × log ⁡ b ( n ) , a = b k n k , a < b k T(n)=\left\{\begin{aligned} &n ^{\log_b(a)}, &a>b^k\\ &n^k \times \log_b(n),&a=b^k\\ &n^k , &a<b^k\\ \end{aligned}\right. T(n)=nlogb(a),nk×logb(n),nk,a>bka=bka<bk

第三章 栈、队列和数组

出栈不同排列个数 卡特兰数: C 2 n n n + 1 \cfrac{C^n_{2n}}{n+1} n+1C2nn

第四章 串

模式匹配KMP

const int Size = 1e6 + 10;
int mynext[Size];
void get_next(string t) {
	t += ' ';
	int n = t.size(), i = 0, j = -1;
	mynext[0] = -1;
	while (i < n) {
		if (j == -1 || t[i] == t[j]) {
			mynext[++i] = ++j;
		}
		else j = mynext[j];
	}
}

vector<int> get_all_point(string s, string t) {
	vector<int> ans;
	int i = 0, j = 0;
	int sn = s.size(), tn = t.size();
	t += ' ';
	while (i < sn && j <= tn) {
		if (j == -1 || s[i] == t[j]) {
			i++; j++;
		}
		else {
			j = mynext[j];
		}
		if (j == tn)ans.push_back(i - tn);

	}
	return ans;
}

第五章 树和二叉树

基本性质

假设总结点数是 n n n,度为 0 0 0的点有 n 0 n_0 n0个,度为 1 1 1的点有 n 1 n_1 n1个,度为 x x x的点有 n x n_x nx个:

  • n = n 0 + n 1 + n 2 + . . . + n x n=n_0+n_1+n_2+...+n_x n=n0+n1+n2+...+nx
  • n − 1 = n 1 + 2 n 2 + 3 n 3 + . . . + x n x n-1=n_1+2n_2+3n_3+...+xn_x n1=n1+2n2+3n3+...+xnx

第二条等式成立的理由:
左边=边数=总结点n-1
右边=度为0的点往下无边,度为1的结点往下连着一条边,度为2的结点往下连着两条边,度为x的结点往下连着x条边

当该树为二叉树时:

  • n = n 0 + n 1 + n 2 , n − 1 = n 1 + 2 n 2 n=n_0+n_1+n_2,n-1=n1+2n_2 n=n0+n1+n2,n1=n1+2n2 ⇒ \Rightarrow n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1

高度为hm叉树的最多结点个数n:
n = m h − 1 m − 1 n=\cfrac {m^h-1}{m-1} n=m1mh1

m叉树的结点个数为n,最低高度h:
n = m h − 1 m − 1 n=\cfrac {m^h-1}{m-1} n=m1mh1可得 h = log ⁡ m ( n ( m − 1 ) ) h=\log_m(n(m-1)) h=logm(n(m1))

先中后序非递归遍历

二叉树定义

// Definition for a binary tree node.
struct TreeNode
{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

先序

vector<int> preorderTraversal(TreeNode* root) {
	TreeNode* p = root;
	vector<int>ans;
	stack< TreeNode*>sk;
	while (p || sk.size()) {
		while (p) {
			sk.push(p);
		    ans.push_back(p->val);
			p = p->left;
		}
		p = sk.top();
		sk.pop();
		p = p->right;
	}
	return ans;
}

中序

vector<int> inorderTraversal(TreeNode* root) {
	TreeNode* p = root;
	vector<int>ans;
	stack< TreeNode*>sk;
	while (p || sk.size()) {
		while (p) {
			sk.push(p);
			p = p->left;
		}
		p = sk.top();
		sk.pop();
		ans.push_back(p->val);
		p = p->right;
	}
	return ans;
}

后序

待续

树、森林、二叉树的转换

规则:左孩子右兄弟
性质:树T转成对应的二叉树BT,则T的后序遍历和BT的中序遍历相同。

第六章 图

性质:假设无权图 G G G的邻接矩阵为 A A A,则 A x A^x Ax的第i行第j列表示的含义是 i 到 j 的 走 k 步 的 路 径 数 量 = A i j i到j的走k步的路径数量=A_{ij} ijk=Aij

第七章 查找

二叉平衡搜索树

高为h的二叉平衡搜索树最少的结点数:
当它的一棵子树高度为h-1,另一棵子树高度为h-2,既平衡又结点最少

int f(int n)
{
	if (n <= 1)
		return n;
	else
		return 1 + f(n - 1) + f(n - 2);
}

以上代码可用矩阵快速幂优化:
[ 1 1 1 1 0 1 0 0 1 ] × [ f n − 1 f n − 2 1 ] = [ f n − 1 + f n − 2 + 1 f n − 1 1 ] = [ f n f n − 1 1 ] \left[ \begin{matrix} 1 &1 &1 \\1 & 0 &1 \\ 0&0&1 \end{matrix} \right] \times \left[ \begin{matrix} f_{n-1}\\f_{n-2} \\1 \end{matrix} \right]=\left[ \begin{matrix} f_{n-1}+f_{n-2}+1\\f_{n-1} \\1 \end{matrix} \right] =\left[ \begin{matrix} f_{n}\\f_{n-1} \\1 \end{matrix} \right] 110100111×fn1fn21=fn1+fn2+1fn11=fnfn11

红黑树

红黑树

B树

高、关键字数和结点数的关系

m阶B树 ,每个结点关键字范围:
①根: [ 1 , m − 1 ] [1,m-1] [1,m1]
②非根: [ ⌊ m − 1 2 ⌋ , m − 1 ] [\lfloor \cfrac{m-1}{2}\rfloor,m-1] [2m1,m1]

x = ⌊ m − 1 2 ⌋ x=\lfloor \cfrac{m-1}{2}\rfloor x=2m1,x为非根最少关键字,则其有(x+1)个子树
mh高B树,总结点数范围:
[ 1 + 2 × ( x + 1 ) h − 1 − 1 x , m h − 1 m − 1 ] [1 + 2 \times \cfrac{(x+1)^{h-1}-1}{x},\cfrac{m^{h}-1}{m-1}] [1+2×x(x+1)h11,m1mh1]

说明

最少:
第一层: 1 1 1
第二层: 2 2 2
第三层: 2 × ( x + 1 ) 2 \times (x+1) 2×(x+1)
第四层: 2 × ( x + 1 ) × ( x + 1 ) 2 \times (x+1)\times (x+1) 2×(x+1)×(x+1)
第 h 层: 2 × ( x + 1 ) h − 1 2 \times (x+1)^{h-1} 2×(x+1)h1
最多:
第一层: 1 1 1
第二层: m m m
第三层: m × m m\times m m×m
第 h 层: m h − 1 m^{h-1} mh1

mh高B树,总关键字n数范围:
[ 2 × ( x + 1 ) h − 1 − 1 , m h − 1 ] [2 \times(x+1)^{h-1}-1,m^{h}-1] [2×(x+1)h11,mh1]

说明

最少:
第一层: 1 1 1
第二层: 2 × x 2\times x 2×x
第三层: 2 × ( x + 1 ) × x 2 \times (x+1)\times x 2×(x+1)×x
第四层: 2 × ( x + 1 ) × ( x + 1 ) × x 2 \times (x+1)\times (x+1)\times x 2×(x+1)×(x+1)×x
第 h 层: 2 × ( x + 1 ) h − 1 × x 2 \times (x+1)^{h-1}\times x 2×(x+1)h1×x
最多:
第一层: 1 × ( m − 1 ) 1\times(m-1) 1×(m1)
第二层: m × ( m − 1 ) m\times(m-1) m×(m1)
第三层: m × m × ( m − 1 ) m\times m\times(m-1) m×m×(m1)
第 h 层: m h − 1 × ( m − 1 ) m^{h-1}\times(m-1) mh1×(m1)

分别令 n = 2 × ( x + 1 ) h − 1 − 1 , m h − 1 n=2 \times(x+1)^{h-1}-1,m^{h}-1 n=2×(x+1)h11,mh1,可得mn个关键字B树,高度h范围:
[ log ⁡ m ( n + 1 ) , log ⁡ x + 1 ( n + 1 2 ) + 1 ] [\log _{m} (n+1),\log _{x+1} (\cfrac{n+1}{2})+1] [logm(n+1),logx+1(2n+1)+1]

插入

待续

删除

待续

第八章 排序

内 部 排 序 { 插 入 排 序 { 直 接 插 入 排 序 折 半 交 换 排 序 希 尔 排 序 交 换 排 序 { 冒 泡 排 序 快 速 排 序 选 择 排 序 { 简 单 选 择 排 序 堆 排 序 归 并 排 序 基 数 排 序 内部排序\left\{\begin{aligned} &插入排序\left\{\begin{aligned} &直接插入排序\\ &折半交换排序\\ &希尔排序\\ \end{aligned}\right.\\ &交换排序\left\{\begin{aligned} &冒泡排序\\ &快速排序\\ \end{aligned}\right.\\ &选择排序 \left\{\begin{aligned} &简单选择排序\\ &堆排序\\ \end{aligned}\right.\\ &归并排序\\ &基数排序\\ \end{aligned} \right. {{

各种算法性质

算法种类时间复杂度空间复杂度稳定性
最好情况平均情况最坏情况
直接插入排序O(n)O(n^2)O(n^2)O(1)稳定
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
简单选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
希尔排序O(nlogn)O(n^2)O(n^1.3)O(1)不稳定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(r)稳定

直接插入排序

选取第一个无序的数,插入到有序的数中
稳定,越顺越快

void InsertSort(int n) {
	//n个数,从0到n-1
	int t, i, j;
	for (i = 0; i < n; i++) {
		t = num[i];
		for (j = i; j > 0 && t < num[j - 1]; j--)
			num[j] = num[j - 1];
		num[j] = t;
	}
}

冒泡排序

从前往后或者从后往前,大的数到后面或者小的数到前面
稳定,越顺越快

简单选择排序

选取一个最小的数(或最小)和当前位置交换
不稳定,时间固定

希尔排序

根据增量分组,在小组中排序(小组中排序采用直接插入排序),持续缩小增量,直到增量为1
稳定,越顺越快

void ShellSort(int n) {
	//n个数,从0到n-1
	int d, i, j, t;
	for (d = n / 2; d >= 1; d--) {
		for (i = 0; i < n; i++) {
			t = num[i];
			for (j = i; j - d >= 0 && t < num[j - d]; j -= d) {
				num[j] = num[j - d];
			}
			num[j] = t;
		}
	}
}

快速排序

选取第一个数为哨兵,小的放到左,大的到右
不稳定,越顺越慢

void QuickSort(int l, int r) {
	//对[l,r]进行排序
	if (l >= r)return;
	int t = num[l], i = l , j = r;
	while (i < j) {
		while (i < j && t <= num[j])j--;
		if (i < j)num[i++] = num[j];
		while (i < j && num[i] <= t)i++;
		if (i < j)num[j--] = num[i];

	}
	num[i] = t;
	QuickSort(l, i - 1);
	QuickSort(i + 1, r);
}

堆排序

构建大根堆或者小根堆,首次调整从最后一个非叶结点开始(从右往左,从下到上)调整(和子节点比较),向上至根节点进行调整
不稳定

void HeapJust(int x, int n) {
	//自顶向下调整
	//父x->子y=2*x+1,2*x+2
	//子y->父x=(y-1)/2
	int y;
	while (x * 2 + 1 <= n - 1) {
		if (2 * x + 2 > n - 1 || num[2 * x + 1] > num[2 * x + 2])y = 2 * x + 1;
		else y = 2 * x + 2;
		if (num[x] > num[y])break;
		swap(num[x], num[y]);
		x = y;
	}
}
void HeapSort(int n) {
	//n个数,从0到n-1
	//从小到大排序,大根堆
	int i;
	for (i = n / 2 - 1; i >= 0; i--) {
		HeapJust(i, n);
	}
	for (i = n - 1; i >= 0; i--) {
		swap(num[i], num[0]);
		HeapJust(0, i);
	}
}

归并排序

划分两个区间,先将这两个区间排序,再将两个区间归并
稳定

int num2[Size];
void MergeSort(int l, int r) {
	
	if (l >= r)return;
	int mid = (l + r) / 2;
	MergeSort(l, mid);
	MergeSort(mid + 1, r);
	int i, l1 = l, r1 = mid, l2 = mid + 1, r2 = r, k = l;
	for (i = l; i <= r; i++)num2[i] = num[i];
	while (l1 <= r1 || l2 <= r2) {
		if (l2 > r2 || l1 <= r1 && num2[l1] < num2[l2])num[k++] = num2[l1++];
		else num[k++] = num2[l2++];
	}
}

基数排序

从按位排序,从低位到高位
稳定,和位数有关

//不超过十位的整数排序
void BaseSort(int n) {
	//n个数,从0到n-1
	int minx = num[0], maxx = num[0],k;
	int i;
	for (i = 1; i < n; i++) {
		minx = min(minx, num[i]);
		maxx = max(maxx, num[i]);
	}
	for (i = 0; i < n; i++) {
		num[i] -= minx;
	}
	int wei[15] = { 1,10,100,1000,10000 ,100000,1000000,10000000,100000000,1000000000 };
	for (int j = 0; j < 10; j++) {
		vector<vector<int>>base(10);
		for (i = 0; i < n; i++) {
			base[(num[i] / (wei[j ]))%10].push_back(num[i]);
		}

		k = 0;	
		for (auto v : base) {
			for (auto x : v) {
				num[k++] = x;
			}
		}
	}
	for (i = 0; i < n; i++) {
		num[i] += minx;
	}
}

多路平衡归并与败者树

假设初始归并段个数 r r r归并路数 k k k,有 n n n个数需要归并, S S S归并趟数
直接选择排序比较次数
S ( n − 1 ) ( k − 1 ) = log ⁡ k r ( n − 1 ) ( k − 1 ) = log ⁡ 2 r log ⁡ 2 k ( n − 1 ) ( k − 1 ) S(n-1)(k-1)=\log_kr(n-1)(k-1)=\cfrac{\log_2r}{\log_2k}(n-1)(k-1) S(n1)(k1)=logkr(n1)(k1)=log2klog2r(n1)(k1)

败者树深度 log ⁡ 2 k \log_2k log2k
败者树比较次数
S ( n − 1 ) log ⁡ 2 k = ( n − 1 ) log ⁡ r k = ( n − 1 ) log ⁡ 2 r S(n-1)\log_2k=(n-1)\log_rk=(n-1)\log_2r S(n1)log2k=(n1)logrk=(n1)log2r

置换选择排序

用来产生更长的初始归并段

最佳归并树

(多叉哈夫曼树
用于组织长度不等的初始归并段的归并顺序,使得WPL最小。
{ n = n 0 + n k n − 1 = k n k ⇒ n 0 − 1 = ( k − 1 ) n k ⇒ ( n 0 − 1 ) % ( k − 1 ) = 0 \left\{\begin{aligned} n=n_0+n_k\\ n-1=kn_k\\ \end{aligned}\right. \Rightarrow n_0-1=(k-1)n_k \Rightarrow (n_0-1)\%(k-1)=0 {n=n0+nkn1=knkn01=(k1)nk(n01)%(k1)=0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值