数据结构笔记

重点

一、数据结构的定义

逻辑结构

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

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

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

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

存储结构

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

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

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

在这里插入图片描述

二、线性表

在这里插入图片描述

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…

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…

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是面向数字基础设施的操作系…

【图论】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…

《一》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;这些抗拒上班的年轻教师群体…

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…

分布式向量数据库-安装部署

下载 GitHub - pgvector/pgvector: Open-source vector similarity search for Postgres 源码编译 ##文件解压缩 unzip pgvector-0.6.2.zip ##编译 make && make install 功能验证 #安装扩展CREATE EXTENSION vector;#创建测试表CREATE TABLE items (id bigseri…

【算法】哈希表

个人主页 &#xff1a; zxctscl 如有转载请先通知 题目 1. 1. 两数之和1.1 分析1.2 代码 2. 面试题 01.02. 判定是否互为字符重排2.1 分析2.2 代码 3. 217. 存在重复元素3.1 分析3.2 代码 4. 219. 存在重复元素 II4.1 分析4.2 代码 5. 49. 字母异位词分组5.1 分析5.2 代码 1. 1…

Gateway的简单介绍和使用

1、Gateway简介&#xff1a; Gateway 是一种 API 网关&#xff08;API Gateway&#xff09;技术&#xff0c;它作为微服务架构中的关键组件&#xff0c;负责为系统的外部请求与内部服务之间提供统一的接入点。Spring Cloud Gateway 是基于 Spring 生态系统实现的一个高性能、易…

2024.4.11

1.思维导图 2.指针形式验证大小端存储 #include<myhead.h>int main(int argc, const char *argv[]) {int num 0x12345678;char* ptr (char *)&num;if(*ptr 0x12){printf("big endian\n");}else if(*ptr 0x78){printf("little endian\n");}r…

MINI2440 开发板 给他干出来了

环境是ubuntu14.04。不要问我为什么是这个版本&#xff0c;因为之前的ubuntu12.04 环境干不出来&#xff0c;你去试试就知道了&#xff01;各种资源包下载不下来。 输入启动参数&#xff1a; 进入MINI2440&#xff1a;别说心里一万个开心&#xff0c;启动完成&#xff0c;输入p…

【电子通识】热风枪的结构与使用方法

热风枪的结构 热风枪是专门用来拆焊、焊接贴片元器件和贴片集成电路的焊接工具&#xff0c;它主要由主机和热风焊枪两大部分构成。 热风枪主要有电源开关、风速设置、温度设置、热风连接等部件组成。根据不同品牌和价位的热风枪&#xff0c;有一些功能齐全的也集成了烙铁功能。…