数据结构笔记

重点

一、数据结构的定义

逻辑结构

集合结构除了同属于一个集合之外,没有其他关系

线状结构数据元素之间是一对一的关系

树形结构数据元素之间是一对多的层次关系

图形结构数据元素之间是多对多的关系

存储结构

线性存储结构数据元素存放在连续的存储单元里,其数据间的逻辑关系和物理关系是一致的

链式存储结构数据元素存放在任意的存储单里,存储单元可以是连续的,也可以是不连续的

分析算法的世界复杂度和空间复杂度

在这里插入图片描述

二、线性表

在这里插入图片描述

1、顺序表和链表的优缺点

顺序表

优点:

  1. 顺序表的内存空间连续。
  2. 尾插、尾删效率较高,时间复杂度是O(1)。
  3. 支持随机访问,可以高效的按下标进行操作,时间复杂度是O(1)。

缺点:

  1. 在顺序表中间插入或删除元素时都涉及到元素的移动,效率较低,时间复杂度为O(N)。
  2. 顺序表长度固定,有时需要扩容。

链表

优点:

  1. 链表的内存空间不连续。

  2. 如果知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(1);

  3. 如果不知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(N)。

  4. 头插、头删的效率高,时间复杂度是O(1)。

  5. 没有空间限制,不会溢出,可以存储很多元素。

缺点:
链表不支持随机访问,查找元素效率低,需要遍历节点,时间复杂度是O(n)。

2、考察链表或者双向链表

1.带头还是不带头

在这里插入图片描述

2.循环还是不循环

在这里插入图片描述

3.单向还是双向

在这里插入图片描述

  • 虽然单链表的结构众多,但大部分常用的还是两种结构。无头单向不循环链表和带头双向循环链表

例题

约瑟夫环

将链表各个元素排在一个圆上,从头开始数,数到第k个元素就删掉,直到链表只有一个元素为止。

struct node
{int data;node *next;
};
node *creat(int n)
{node *head, *tail, *p;head = new node;tail = new node;p = new node;p -> data = 1;p -> next = NULL;head = p;tail = p;for(int i = 2; i <= n; i ++){p = new node;p -> data = i;p -> next = NULL;tail -> next = p;tail = p;}tail -> next = head;return head;
}
void del(node *head, int n, int m)
{int cnt = 0, s = 0;node *pre, *p;pre = head;while(pre -> next != head){pre = pre -> next;}while(cnt < n - 1){p = pre -> next;s ++;if(s == m){s = 0;cnt ++;pre -> next = p -> next;cout << p -> data << " ";delete(p);}else pre = p;}cout << pre -> data << endl;
}

单链表为啥要设置头节点

  • 有了头结点后****,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。****
  • 头指针具有标识作用,故常用头指针冠以链表的名字。
  • *为了使空链表与非空链表处理一致,我们通常设一个头结点*

三、栈与队列

入出栈都采取先进后出原则。

中缀式转成后缀式

/*
首先输入字符串,然后遍历字符串进行操作
当当前字符为数字时直接输出
当当前字符为*或/时,输出栈中的*或/号,当前字符入栈
当当前字符为+或-时,输出栈中的字符直到字符为(为止,当前字符入栈
当当前字符为(时,入栈
当当前字符为)时,输出栈中字符直到(为止
*/

后缀式求值

设置一个栈,开始时,栈为空,然后从左到右扫描后缀表达式,若遇操作数,则进栈;若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的 放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕

5 -2 + 3 * #
遇到5, -2,就push到栈中,此时栈中有两个元素,为5, -2,遇到+,从栈顶中取出两个元

素,进行运算,5 +(-2) = 3,获得结果之后,将结果push到栈中,现在栈中只剩下了一个元素

3,然后继续输入,3,push到栈中,此时栈中有两个元素,遇到 ‘*’ 号,从栈顶中取出两个元素,

3, 3,进行乘法运算,3 * 3 = 9,然后将 9 push到栈中,栈中只剩下了一个元素,9,然后继续

输入,此时遇到了 ‘#’ 号,结束读入。
括号匹配

当当前字符为(、【、{时,将括号入栈,当当前字符为)、】、}时,查看栈顶元素是否为其对应的括号,同时栈不可以为空

队列

队列是先进先出

用队列解决约瑟夫环问题

将所有元素入队列,

int main()
{int n, m;cin >> n >> m;queue<int>q;for(int i = 1; i <= n; i ++)q.push(i);while(q.size() > 1){for(int i = 0; i < m - 1; i ++){int now = q.front();q.pop();q.push(now);}cout << q.front() << " ";q.pop();}cout << q.front() << endl;return 0;
}

四、kmp

模板

string s, t;
int n, m;
int Next[N];
void dp()
{int i = 0, j = -1;Next[0] = -1;while(i < m){if(j == -1 || t[i] == t[j]){i ++;j ++;Next[i] = j;}else j = Next[j];}
}
int kmp()
{int i = 0, j = 0;int cnt = 0;while(i < n && j < m){if(j == -1 || s[i] == t[j]){i ++;j ++;}else j = Next[j];if(j == m){cnt ++;j = Next[j];}}return cnt;
}

五、广义表知识点

广义表的基础概念

  1. 什么是广义表

    广义表,又称列表,也是一种线性存储结构,既可以存储不可再分的元素,也可以存储广义表,记作:LS = (a1,a2,…,an),其中,LS 代表广义表的名称,an 表示广义表存储的数据,广义表中每个 ai 既可以代表单个元素,也可以代表另一个广义表。

  2. 广义表的原子和子表

    广义表中存储的单个元素称为 “原子”,而存储的广义表称为 “子表”。
    例如 :广义表 LS = {1,{1,2,3}},则此广义表的构成 :广义表 LS 存储了一个原子 1 和子表 {1,2,3}。
    广义表存储数据的一些常用形式:
    A = ():A 表示一个广义表,只不过表是空的。
    B = (e):广义表 B 中只有一个原子 e。
    C = (a,(b,c,d)) :广义表 C 中有两个元素,原子 a 和子表 (b,c,d)。
    D = (A,B,C):广义表 D 中存有 3 个子表,分别是A、B和C。这种表示方式等同于 D = ((),(e),(b,c,d)) 。
    E = (a,E):广义表 E 中有两个元素,原子 a 和它本身。这是一个递归广义表,等同于:E = (a,(a,(a,…)))。

  3. 广义表的表头和表尾

    当广义表不是空表时,称第一个数据(原子或子表)为"表头",剩下的数据构成的新广义表为"表尾"。
    除非广义表为空表,否则广义表一定具有表头和表尾,且广义表的表尾一定是一个广义表。

广义表的存储结构

求广义表长度时,两种不同的存储方式求解也有所不同,如下示意图所示:

在这里插入图片描述

对于图 1a) 来说,只需计算最顶层(红色标注)含有的节点数量,即可求的广义表的长度。同理,对于图 1b) 来说,由于其最顶层(蓝色标注)表示的此广义表,而第二层(红色标注)表示的才是该广义表中包含的数据元素,因此可以通过计算第二层中包含的节点数量,才可求得广义表的长度。

在这里插入图片描述

六、树和二叉树

先序中序后序

根左右,左根右,左右根

在这里插入图片描述

在这里插入图片描述

层序遍历

从上到下,从左到右遍历

void cengxu(node *root)
{int in = 0, out = 0;node *q[55];q[in ++] = root;while(in > out){if(q[out]){cout << q[out] -> data;q[in ++] = q[out] -> l;q[in ++] = q[out] -> r;}out ++;}
}

以上算法都是时间复杂度o(n),空间复杂度o(n)

还原二叉树、创建二叉树

还原二叉树

先序遍历字符串输入,如果为叶子节点输出NULL,否则创造新节点,左建树,右建树

struct node
{int data;node *l, *r;
};
char a[N];
int k = 0;
node *build()
{node *root;if(a[k ++] == ',') return NULL;root = new node;root -> data = a[k];root -> l = build();root -> r = build();return root;
}

创建二叉树

先序中序输出后序

struct node
{int data;node *l, *r;
};
char pre[N], mid[N];
node *build(int len, char *pre, char *mid)
{if(!len) return NULL;node *root = NULL;root = new node;root -> data = pre[0];int i;for(i = 0; i < len; i ++){if(mid[i] == pre[0])break;}root -> l = build(i, pre + 1, mid);root -> r = build(len - i - 1, pre + i + 1, mid + 1 + i);return root;
}
void postorder(node *root)
{if(root){postorder(root -> l);postorder(root -> r);cout << char(root -> data);}
}

中序和后序输出先序

struct node
{int data;node *l, *r;
};
char mid[N], post[N];
node *build(int len, char *mid, char *post)
{if(!len) return NULL;node *root = NULL;root = new node;root -> data = post[len - 1];int i;for(i = 0; i < len; i ++){if(mid[i] == post[len - 1])break;}root -> l = build(i, mid, post);root -> r = build(len - 1 - i, mid + i + 1, post + i);return root;
}
void preorder(node *root)
{if(root){cout << char(root -> data);preorder(root -> l);preorder(root -> r);}
}

二叉树的六个性质

性质1:二叉树第i层上的结点数目最多为2^(i-1)(i>=1)

性质2:深度为i的二叉树至多有2 ^(i)-1个结点,至少有2 ^(i-1)个结点(i>=1)

性质3:包含n个结点的二叉树的高度至少为
( l o g 2 ​ n ) + 1 (log2​ n)+1 (log2​n)+1
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

证明:
n为总结点数,n1为度为1的结点总数,n0,n2同理;
由(1)(2)

n=n0+n1+n2 (1)
n=n0 *0+n1 *1+n2 2+1即 n=n1+2n2+1 (2)

得:n0=n2+1

性质5:具有n个结点的完全二叉树的深度为
f l o o r ( l o g 2 n ) ( 向下取整 ) + 1 floor(log2n)(向下取整)+1 floor(log2n)(向下取整)+1
性质6:将一颗完全二叉树依次编号1-n;
结点编号间关系:

                                            floor(i/2)|i/   \  2i      2i+1

求深度求叶子

求深度

int deep(node *root)
{int d1, d2;if(root){d1 = deep(root -> l);d2 = deep(root -> r);return max(d1, d2) + 1;}return 0;
}

求叶子

出度为0的为叶子

树与二叉树,树二叉树和森林之间的转化

  1. 将树转换为二叉树:树中每个结点最多只有一个最左边的孩子(长子)和一个右邻的兄弟。按照这种关系很自然地就能将树转换成相应的二叉树:1.在所有兄弟结点之间加一连线2.对每个结点,除了保留与其长子的连线外,去掉该结点与其它孩子的连线。如下图所示:

    在这里插入图片描述

  2. 将一个森林转换为二叉树:

    具体方法是:1.将森林中的每棵树变为二叉树;2.因为转换所得的二叉树的根结点的右子树均为空,故可将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树。

    如下图所示:

    在这里插入图片描述

  3. 二叉树转换为树:

    是树转换为二叉树的逆过程。

    1.加线。若某结点X的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点…,都作为结点X的孩子。将结点X与这些右孩子结点用线连接起来。

    2.去线。删除原二叉树中所有结点与其右孩子结点的连线。

    如下图所示:

    在这里插入图片描述

  4. 二叉树转换为森林:

    假如一棵二叉树的根节点有右孩子,则这棵二叉树能够转换为森林,否则将转换为一棵树。

    1.从根节点开始,若右孩子存在,则把与右孩子结点的连线删除。再查看分离后的二叉树,若其根节点的右孩子存在,则连线删除…。直到所有这些根节点与右孩子的连线都删除为止。

    2.将每棵分离后的二叉树转换为树。

    如下图所示:

    在这里插入图片描述

七、图论

基本算法:图的邻接矩阵存储,图的邻接表存储,图的正向反向转化,会画邻接矩阵邻接表,有向图会画逆邻接表,还原图,bfs(分层,dp,最短路)和dfs(图的连通集)的算法

最小生成树的prime算法和克鲁斯卡尔算法

最短路的迪杰斯特拉算法,floyd, 拓扑排序算法,欧拉回路

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

邻接矩阵和邻接表存储知识点

邻接矩阵就是一个二维数组存储信息

邻接表就是vector<vector>mp;

邻接矩阵适用于稠密图(边数接近于顶点数的平方),邻接表适用于稀疏图(边数远小于顶点数的平方)。

bfs

存储

BFS是一种借用队列来存储的过程,分层查找,优先考虑距离出发点近的点。无论是在邻接表还是邻接矩阵中存储,都需要借助一个辅助队列,v个顶点均需入队,最坏的情况下,空间复杂度为O(v)。

邻接表形式存储时,每个顶点均需搜索一次,时间复杂度T1=O(v),从一个顶点开始搜索时,开始搜索,访问未被访问过的节点。最坏的情况下,每个顶点至少访问一次,每条边至少访问1次,这是因为在搜索的过程中,若某结点向下搜索时,其子结点都访问过了,这时候就会回退,故时间复 杂度为O(E),算法总的时间复 度为O(|V|+|E|)。

邻接矩阵存储方式时,查找每个顶点的邻接点所需时间为O(V),即该节点所在的该行该列。又有n个顶点,故算总的时间复杂度为O(|V|^2)。

算法实现

将起点放到队列里,从队列头节点开始遍历,将与他连着的点放到队列里,然后继续遍历队列头节点,重复之前的,把与他连着的点放到队列里,最后遍历完成,全部节点入队

void bfs(int op)
{queue<int>q;q.push(op);vis[op] = 1;while(q.size()){int now = q.front();q.pop();cout << now << " ";for(int i = 0; i < mp[now].size(); i ++){if(!vis[mp[now][i]]){vis[mp[now][i]] = 1;q.push(mp[now][i]);}}}
}

dfs

存储

DFS算法是一一个递归算法,需要借助一个递归工作栈,故它的空问复杂度为O(V)。

遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所采用结构。

邻接表表示时,查找所有顶点的邻接点所需时间为O(E),访问顶点的邻接点所花时间为O(V),此时,总的时间复杂度为O(V+E)。

邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(V),要查找整个矩阵,故总的时间度为O(V^2)。

v为图的顶点数,E为边数。

算法实现

将起点放到队列里,然后遍历队列,取出头节点,搜索与他连着的点,然后搜索与他连着的点的连着的点,然后是连着的点的连着的点的连着的点。。。。。

vector<int>mp[N];
int vis[N];
int n, m;
void dfs(int op)
{if(op > n) return;cout << op << " ";vis[op] = 1;for(int i = 0; i < mp[op].size(); i ++){if(!vis[mp[op][i]]){dfs(mp[op][i]);}}
}
int main()
{cin >> n >> m;for(int i = 1; i <= m; i ++){int u, v;cin >> u >> v;mp[u].push_back(v);}for(int i = 0; i < n; i ++)sort(mp[i].begin(), mp[i].end());for(int i = 0; i < n; i ++){if(!vis[i]){dfs(i);}}return 0;
}

最小生成树

克鲁苏卡尔

将所有边排序,从小到大依次加边,加边过程中不可以成环,直到加完n-1条边

struct node
{int u, v, w;
}a[N];
int f[N];
bool cmp(node a, node b)
{return a.w < b.w;
}
int Find(int x)
{return x == f[x] ? x : f[x] = Find(f[x]);
}
int Merge(int x, int y)
{int a = Find(x);int b = Find(y);if(a != b){f[b] = a;return 1;}return 0;
}
int main()
{int n, m;cin >> n >> m;for(int i = 0; i <= n; i ++)f[i] = i;for(int i = 1; i <= m; i ++){int u, v, w;cin >> u >> v >> w;a[i].u = u;a[i].v = v;a[i].w = w;}sort(a + 1, a + 1 + m, cmp);int cnt = 0, ans = 0;for(int i = 1; i <= m; i ++){int x = a[i].u, y = a[i].v;if(Merge(x, y)){cnt ++;ans += a[i].w;}}if(cnt != n - 1) puts("-1");else cout << ans << endl;return 0;
}

普利姆算法

从起点开始遍历,将这个点看作一个集合,然后找离这个集合最近的一个点,然后将这个点放入集合,然后再重复操作。

int mp[N][N];
int vis[N];
int dis[N];
int n, m;
int prime()
{mem(dis, INF);dis[1] = 0;int ans = 0;for(int i = 1; i <= n; i ++){int u = -1, Min = INF;for(int j = 1; j <= n; j ++){if(!vis[j] && dis[j] < Min){u = j;Min = dis[j];}}if(u == -1) return -1;vis[u] = 1;ans += dis[u];for(int j = 1; j <= n; j ++){if(!vis[j] && mp[u][j] != INF && dis[j] > mp[u][j]){dis[j] =  mp[u][j];}}}return ans;
}
int main()
{mem(mp, INF);cin >> n >> m;while(m --){int u, v, w;cin >> u >> v >> w;mp[u][v] = w;mp[v][u] = w;}cout << prime();return 0;
}

最短路

迪杰斯特拉

朴素版迪杰斯特拉 O(n^2)

vector<PII>mp[20010];
int dis[20010], vis[20010];
int n, m;
void dijkstra()
{mem(dis, 0x3f);dis[0] = 0;for(int i = 1; i <= n; i ++){int u = -1, Min = 999999;for(int j = 0; j < n; j ++){if(!vis[j] && Min > dis[j]){u = j;Min = dis[j];}}if(u == -1) break;vis[u] = 1;for(int j = 0; j < mp[u].size(); j ++){int v = mp[u][j].xx;int w = mp[u][j].yy;if(!vis[v]){dis[v] = min(dis[v], dis[u] + w);}}}
}
int main()
{cin >> n >> m;while(m --){int u, v, w;cin >> u >> v >> w;mp[u].push_back({v, w});}dijkstra();for(int i = 1; i < n; i ++){if(dis[i] != INF){cout << dis[i] << " ";}}return 0;
}

floyd

	for(int k = 1; k <= n; k ++){for(int i = 1; i <= n; i ++){for(int j = 1; j <= n; j ++){f[i][j] = min(f[i][j], f[i][k] + f[k][j]);}}}

拓扑排序

将所有点统计入度,入度为0的点为根节点,将根节点入队列,然后取出头节点,删去头节点,将头节点连着的每一个点入度都减一,当减到0时入队,直到元素遍历完,入队顺序就是拓扑顺序。

时间复杂度:O(n + e)

空间复杂度:O(n)

void toop()
{queue<int>q;for(int i = 0; i < n; i ++){if(in[i] == 0)q.push(i);}int cnt = 0, ans = 0;while(q.size()){int now = q.front();q.pop();cnt ++;for(int i = 0; i < n; i ++){if(mp[now][i] != -1){in[i] --;if(in[i] == 0)q.push(i);dis[i] = max(dis[now] + mp[now][i], dis[i]);ans = max(dis[i], ans);}}}if(cnt != n) puts("Impossible");else cout << ans << endl;
}

欧拉回路

度全都是偶数即为欧拉图

八、查找表

静态:折半;动态:二叉排序树

二分查找

//分成[l, mid][mid + 1, r]找min
int erfen()
{int l = 0, r = INF;while(l < r){int mid = l + r >> 1;if(check(mid))r = mid;else l = mid + 1;}return l;
}
// 分成[l, mid - 1][mid, r], 找max
int erfen()
{int l = 0, r = INF;while(l < r){int mid = l + r + 1 >> 1;if(check(mid))l = mid;else r = mid - 1;}return l;
}

二叉排序树

左边的儿子一定比根节点小,右边的儿子大于等于根节点

查找

//查找的递归算法
BSTNode *Search(BSTNode *root, int x)
{if(root->data == x){return root;}else if(x < root->data){return Search(root->left, x);}else{return Search(root->right, x);}
}

插入

//插入的递归算法
BSTNode *Insert(BSTNode *root, int x){if(root == NULL){root = CreateTreeNode(x);return root;}if(x < root->data){root->left = Insert(root->left, x);}if(x > root->data){root->right = Insert(root->right, x);}return root;
}

删除

  1. 删叶子节点:直接删
  2. 删有一个子树的:把子树连到要删除的节点的根节点上
  3. 删有两个节点的:找到右子树的最小值,然后用1、2操作删除它,然后安到我们要删的节点上

优秀博客:如何从二叉搜索树中删除节点? (baidu.com)

平均查找长度

  1. 查找成功的平均查找长度

    在这里插入图片描述

  2. 查找不成功的平均查找长度

    在这里插入图片描述

九、哈希表

哈希的存储,线性探测,平方探测,拉链探测;查找成功的平均长度,查找不成功的平均长度,ALS

参考( 哈希.docx)

当哈希表后边没表格的时候,再从头开始找

平均查找长度

精彩博客:哈希表平均查找长度_数据结构哈希表查找长度_好饿呀~~~的博客-CSDN博客

十、排序

基本思想:冒泡,插入,希尔,快排,堆排,归并排序,桶排,基数排序 的思想以及应用,优先级的比较,复杂度的比较,总体性能的比较,对序列初始状态的要求,稳定性的问题。

1.基本流程

快排

i指针放l,j指针放r,j–,直到比al小的时候停止,i++,直到比al大的时候停止,然后互换,最后互换ai和al,让al搁中间,然后再递归排序两边的序列

void q_sort(int l, int r)
{int i = l, j = r;if(l > r) return;while(i < j){while(a[j] >= a[l] && i < j)j --;while(a[i] <= a[l] && i < j)i ++;if(i < j) swap(a[i], a[j]);}swap(a[i], a[l]);q_sort(l, i - 1);q_sort(i + 1, r);
}

归并排序

分成单个区间,然后合并的时候两两排序,直到排好

int tmp[N], a[N], n;
//这里为需要背诵的部分,要求:给一个相关题目,能快速写出该模板并调试通过。
//归并排序
void merge_sort(int q[], int l, int r)
{//递归出口if (l >= r) return;//第一步,分成两个子区间int mid =  l + r >> 1;//第二部,递归处理子区间//要注意这里用的mid和mid+1来划分两区间,建议不要用mid-1和mid来划分merge_sort(q, l, mid);merge_sort(q, mid + 1, r);//第三步,合并排序好的子区间//tips:k为tmp下标,i和j为两个子区间起始位置int k = 0, i = l, j = mid + 1;//排序好的两边取大小,暂存数组tmp挑小的取while (i <= mid && j <= r) {if (q[i] <= q[j]) tmp[k++] = q[i++];else tmp[k++] = q[j++];}//很可能存在有一子区间没有比较完,由于该区间是排好序的,后面的没比较完,说明都是最大的,直接往tmp后面加即可。while (i <= mid) tmp[k++] = q[i++];while (j <= r) tmp[k++] = q[j++];//在[l, r]范围中,将tmp数组存的有序值赋给q数组完排序,注意<=r的等号不要漏for (int i = l, j =0; i <= r; i++, j++) q[i] = tmp[j];
}

堆排序

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

注意:升序用大根堆,降序就用小根堆(默认为升序)

int cnt;
int h[N];
void down(int x)
{int t = x;if (x * 2 <= cnt && h[x * 2] > h[t])t = x * 2;if (x * 2 + 1 <= cnt && h[x * 2 + 1] > h[t])t = x * 2 + 1;if (x != t){swap(h[x], h[t]);down(t);}
}
void heap_sort(int l, int r)
{cnt = 0;for (int i = l; i <= r; i++){h[++cnt] = a[i];}for (int i = cnt / 2; i; i--){down(i);}int k = r;while (cnt){a[k --] = h[1];h[1] = h[cnt--];down(1);}
}

计数排序

计算ai的数量,然后算它在数组中是第几小的,然后把第几小赋给b数组作为下标,同时b下标=a中的值,最后整理下

int a[N], h[N+50];
int b[N];
int n;//如果出现负数 (-10000,10000)整体右移 x+10000 (0,20000)
//如果小数 整体*100
void counting_sort() 
{int w = 100050;memset(h, 0, sizeof h);for (int i = 0; i < n; i++) h[a[i]]++;for (int i = 1; i <= w; i++) h[i] += h[i - 1];for (int i = n-1; i >= 0; i--) b[h[a[i]]--] = a[i];for(int i=1;i<=n;i++)a[i-1]=b[i];
}

桶排序

桶排序的核心思想就是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶排序完之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。

int n, w = 100000, a[N];
vector<int> bucket[N];void insert_sort(vector<int>& A) 
{for (int i = 1; i < A.size(); i++) {int key = A[i];int j = i - 1;while (j >= 0 && A[j] > key) {A[j + 1] = A[j];j--;}A[j + 1] = key;}
}
void bucket_sort() 
{int bucket_size = w / n + 1;for (int i = 0; i < n; i++) {bucket[i].clear();}for (int i = 0; i < n; i++) {bucket[a[i] / bucket_size].push_back(a[i]);}int p = 0;for (int i = 0; i < n; i++) {insert_sort(bucket[i]);for (int j = 0; j < bucket[i].size(); j++) {a[p++] = bucket[i][j];}}
}
// I prefer it
vector<int> bucket[N];
int w = 100000;
void bucket_sort()
{int bucket_size = w/n + 1;for(int i = 0; i < n; i ++)bucket[i].clear();for(int i = 0; i < n; i ++){bucket[a[i]/bucket_size].push_back(a[i]);}int p = 0;for(int i = 0; i < n; i ++){sort(bucket[i].begin(), bucket[i].end());for(auto it : bucket[i])a[p ++] = it;}
}

基数排序

const int N = 100010;
const int W = 100010;
const int K = 100;
int n, w[K], k, cnt[W];
struct Element {int key[K];bool operator<(const Element& y) const {for (int i = 1; i <= k; ++i) {if (key[i] == y.key[i]) continue;return key[i] < y.key[i];}return false;}
} a[N], b[N];
void counting_sort(int p) {memset(cnt, 0, sizeof(cnt));for (int i = 1; i <= n; ++i) ++cnt[a[i].key[p]];for (int i = 1; i <= w[p]; ++i) cnt[i] += cnt[i - 1];for (int i = n; i >= 1; --i) b[cnt[a[i].key[p]]--] = a[i];memcpy(a, b, sizeof(a));
}
void radix_sort() {for (int i = k; i >= 1; --i) {counting_sort(i);}
}

2.各种算法的特点

在这里插入图片描述

堆排序、快速排序、希尔排序、直接选择排序是不稳定的排序算法,而冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/808520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VRTK_强制瞬移/传送

VRTK_强制瞬移/传送 前言配置代码 前言 在使用VRTK制作虚拟仿真项目的时候&#xff0c;会遇到强制头盔至目标点的功能 VRTK内有封装好的移动方法。 VRTK_BasicTeleport脚本内的方法ForceTeleport() 配置 需要配置的传送组件 代码 本文代码是直接可以其他脚本调用&#x…

Python中基于 __del__ 方法销毁对象

函数中的__del__() 方法: 销毁对象 Python 通过调用__init__()方法构造当前类的实例化对象&#xff0c;而__del__() 方法&#xff0c;是用来销毁实例化对象。 事实上在编写程序时&#xff0c;如果之前我们创建的类实例化对象后续程序不再使用&#xff0c;最好在合适位置手动将…

springboot在使用 Servlet API中提供的javax.servlet.Filter 过滤器 对请求参数 和 响应参数 进行获取并记录日志方案

不多说 直接上代码 第一步 package com.xxx.init.webFilter;import com.alibaba.fastjson.JSONObject; import com.xxx.api.constant.CommonConstant; import com.xxx.api.entities.log.OperationLog; import com.xxx.init.utils.JwtHelper; import com.xxx.init.utils.Reques…

c++和java中常用语法对照

1.vector c #include<vector> std::vector<>a;//创建 a[i]//访问,获取 a[i]v;//修改 a.push_back(v)//添加元素 a.erase(a.beginw-1)//删除第w个元素 std::sort(a.begin,a.end); std::reverse(a.begin(),a.end());java import java.util.Vector; Vector<Inte…

antd+Vue 3实现table行内upload文件图片上传【超详细图解】

目录 一、背景 二、效果图 三、代码 一、背景 一名被组长逼着干前端的苦逼后端&#xff0c;在一个晴天霹雳的日子&#xff0c;被要求前端订单产品实现上传产品图片并立刻回显图片。 二、效果图 三、代码 <template><a-table :dataSource"dataSource" :c…

如何使用固定公网地址SSH远程访问本地内网openEuler系统

文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、边缘openEuler是面向数字基础设施的操作系…

javaScript Object.hasOwn()的用法

Object.hasOwn() 如果指定的对象自身有指定的属性&#xff0c;则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在&#xff0c;该方法返回 false。 备注&#xff1a; Object.hasOwn() 旨在取代 Object.prototype.hasOwnProperty()。 **语法&#xff1a;**Objec…

洛谷 P1434滑雪

[SHOI2002] 滑雪 题目描述 Michael 喜欢滑雪。这并不奇怪&#xff0c;因为滑雪的确很刺激。可是为了获得速度&#xff0c;滑的区域必须向下倾斜&#xff0c;而且当你滑到坡底&#xff0c;你不得不再次走上坡或者等待升降机来载你。Michael 想知道在一个区域中最长的滑坡。区域…

【图论】Dijkstra单源最短路径-朴素方法-简单模板(迪杰斯特拉算法)

Dijkstra单源最短路径 问题描述 输入n 表示n个结点&#xff0c;m表示m条边&#xff0c;求编号1的结点到每个点的最短路径 输出从第一个点到第n个点的最短路径 思路 将图g[][]中所有的权值初始化为0x3f表示正无穷 将dist[]中所有的值初始化为0x3f表示从第一个点到所有点的距离…

NX/UG二次开发—CAM—一些外挂刀路选择方案对比

在做一刀轨编辑工具时&#xff0c;大家希望实现类似NX刀轨编辑中选择刀路的功能&#xff0c;以下我罗列了几种目前外挂里使用的几种方式&#xff0c;自己也做了一些对比&#xff1a; 涉及一些运算时间&#xff0c;参考电脑配置(内存32G&#xff0c;CPUi9-12950HX) 1、刀路转成…

PCB封装库的创建及引入

法1 1.创建lib 2.放置 找到你想要画的封装的器件的数据手册了解相关信息。 直插式选Multi-layer 贴片选Top-layer 焊盘尺寸 焊盘空尺寸 法2 嘉立创eda直接copy 再嘉立创中找到你想要的pcb&#xff0c;导出为ad 然后再ad中找到我们导出的文件 复制他 然后再库中粘贴 pcb库…

sky光遇加速器推荐 steam光遇低延迟稳定的加速器推荐

在光遇游戏中&#xff0c;子民指的就是游戏中的人影&#xff0c;玩家在游戏里面需要找到蓝色人影并触碰它&#xff0c;然后跟随光点&#xff0c;这样的话我们就可以看到一个深灰色的石像&#xff0c;点燃石像上的火苗&#xff0c;它就会教我们一个新的互动姿势。玩家找到黄色人…

Python中Python-docx 包的run介绍

先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例&#xff1a; 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是&#xff1a;",len(doc.paragr…

C语言—每日选择题—Day70(需要看)

第一题&#xff08;注&#xff09; 1、关于内存管理&#xff0c;以下有误的是&#xff08; &#xff09; A: malloc在分配内存空间大小的时候是以字节为单位 B: 如果原有空间地址后面还有足够的空闲空间用来分配&#xff0c;则在原有空间后直接增加新的空间&#xff0c;使得增加…

《一》Qt的概述

1.1 什么是Qt Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。 1.2 Qt的发展史 1991年 Qt最早由芬兰奇趣科技开发 1996年 进入商业领域&#x…

未来课堂革命:OpenAI 发布 ChatGPT 使用指南,探索生成式 AI 如何重塑教育景观

随着新学期的来临&#xff0c;众多初登教师舞台的 00 后们&#xff0c;也完成了他们的第一个教师身份下的暑期生活。 对于开学的抵触情绪&#xff0c;不仅学生们普遍存在&#xff0c;许多 00 后的新晋教师们也同样感同身受。某种程度上&#xff0c;这些抗拒上班的年轻教师群体…

Go-学会 Go 中 interface 的基本使用

本节重点&#xff1a; 学会 Go 中 interface 的基本使用 在 Go 中&#xff0c;接口类型是一种抽象类型&#xff0c;是方法的集合&#xff0c;其他类型实现了这些方法就是实现了这个接口。 声明和实现接口 在 Go 中接口的声明如下&#xff1a; /* 定义接口 */ type interface…

Mac上的最佳3D建模工具-犀牛Rhinoceros 8 for Mac v8.6.24101.05002完美兼容激活

Rhino 8是一款计算机辅助设计&#xff08;CAD&#xff09;和三维建模软件&#xff0c;由美国公司McNeel & Associates开发。它是Rhino系列的最新版本&#xff0c;用于创建、编辑、分析、渲染和动画三维模型。 以下是Rhino 8的一些主要特点和功能&#xff1a; 1. **强大的…

Spring Boot与Vue联手打造智能化学生选课平台

末尾获取源码作者介绍&#xff1a;大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与…

Scaffold-GS 代码阅读笔记

1. 系统启动部分 使用 python 中的 parser 库 为配置系统的参数设定, 和3DGS 类似&#xff0c;并且使用safe_state(args.quiet) 函数 为每一次的 log 输出加上对应的 时间戳 ## 配置参数的设定lp ModelParams(parser)op OptimizationParams(parser)pp PipelineParams(pars…