//自溜用
排序
插入排序
遍历 i , i 与前面的比较交换,直到遇到比 i 小的数
for(int i = 1;i < n;i++)
{
int j = i - 1;
int temp = A[i];
while(j >= 0 && A[j] > temp)
{
A[j+1] = A[j];
j--;
}
A[j+1] = temp;
}
冒泡排序
两两交换
for(int i = 1;i < n;i++)
{
for(int j = 0;j < n - i;j++)
{
if(A[j] > A[j+1])
swap(A[j],A[j+1]);
}
}
选择排序
遍历 i ,找到后边比 i 小的Swap,不稳定
归并排序
MergeSort()递归,不断分割成局部数组
merge,通过n1,n2对两个局部数组取值,再整合成一个数组
void merge(int A[],int left,int mid,int right)
{
int n1 = mid - left;
int n2 = right - mid;
for(int i = 0;i < n1;i++) L[i] = A[left + i];
for(int i = 0;i < n2;i++) R[i] = A[mid + i];
L[n1] = BIGNUM;
R[n2] = BIGNUM;
int i = 0,j = 0;
for(int k = left;k < right;k++)
{
if(L[i] < R[j])
{
A[k] = L[i++];
}
else
{
A[k] = R[j++];
}
}
}
void MergeSort(int A[],int left,int right)
{
if(left + 1 < right)
{
int mid = (left + right) / 2;
MergeSort(A,left,mid);
MergeSort(A,mid,right);
merge(A,left,mid,right);
}
}
快排
不断分割
//以中为基准的快排
void qsort(int left,int right)
{
int i = left,j = right,mid = a[(i + j) / 2];
while(i <= j)
{
while(a[i] < mid) i++;
while(a[j] > mid) j--;
if(i <= j)
{
swap(i++,j--);
}
}
if(left < j) qsort(left,j);
if(right > i) qsort(i,right);
}
//以最右为基准的快排
vector<int> part(vector<int>& v, int left,int right)
{
//随机把最后的数和前面的交换,随机基准数,降低数据对算法的影响
swap(v[right], v[rand() % (right - left + 1) + left]);
int less = left, more = right - 1,cur = left;
int x = v[right];
for (; cur <= more; cur++)
{
if (x == v[cur])
continue;
else if (x > v[cur])
{
swap(v[less++], v[cur]);
}
else
{
swap(v[more--], v[cur--]);
}
}
swap(v[right], v[more+1]);
return vector<int>{ less,more };
}
void quicksort(vector<int>& v,int left,int right)
{
if (left < right)
{
vector<int> temp = part(v,left, right);
quicksort(v,left, temp[0]-1);
quicksort(v,temp[1] + 1, right);
}
}
树
普通树
树的结构: 左子右兄
递归设置 深度
二叉树
遍历方式 : 前中后
搜索二叉树
插入节点
Node *x ,*y ,*z;
x = root; //从root开始找
y = NIL; //储存上一个节点
z = new Node(k,NIL,NIL);
//NIL 为 nullptr
//找到 z 该连接的 parent
while(x != NIL)
{
y = x;
if(z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
//连接 z 与 在 z 的parent
if(y == NIL)
{
root = z;
}
else
{
if(z->key < y->key)
y->left = z;
else y->right = z;
}
删除节点
Node * z = nfind(num);
if(z == nullptr)
{
cout << "未找到该节点" << endl;
return;
}
//进行节点的删除
Node * zp = z->parent;
//无子节点
if(z->left == nullptr && z->right == nullptr)
{
if(zp != nullptr)
{
if(z->parent->left == z)
z->parent->left = nullptr;
else
z->parent->right = nullptr;
}
else
{
root = nullptr;
}
delete z;
cout << "delete node" << endl;
return;
}
//单个子节点
if(z->left == nullptr && z->right != nullptr)
{
z->right->parent = z->parent;
if(zp != nullptr)
{
if(z->parent->left == z)
z->parent->left = z->right;
else
z->parent->right = z->right;
}
else
{
root = z->right;
z->right->parent = nullptr;
}
delete z;
cout << "delete node" << endl;
return;
}
else if(z->left != nullptr && z->right == nullptr)
{
z->left->parent = z->parent;
if(zp != nullptr)
{
if(z->parent->left == z)
z->parent->left = z->left;
else
z->parent->right = z->left;
}
else
{
root = z->left;
z->left->parent = nullptr;
}
delete z;
cout << "delete node" << endl;
return;
}
//双节点
if(zp == nullptr)
{
root = z->right;
z->right->parent = nullptr;
}
else
{
if(zp->left == z)
{
zp->left = z->right;
z->right->parent = zp;
}
else
{
zp->right = z->right;
z->right->parent = zp;
}
}
Node* min = getmin(z->right);
min->left = z->left;
z->left->parent = min->left;
delete z;
cout << "delete node" << endl;
线索二叉树
把叶子节点的空闲指针 用来 存储前驱后续
哈夫曼树 | 最优二叉树
一个队列 装 不同的 权值
取出最小的两个,合并为一个新的权值,放到队尾
哈夫曼算法:https://blog.csdn.net/u012011079/article/details/117449109
哈夫曼编码 通过哈夫曼算法可以实现 编码 与 解码
编码:通过栈装01,叶子节点向上进行编码
int cur,father;
stack<char> s;
for(int i = 1;i <= n;i++)
{
string temp = "";
cur = i;
father = T[cur].parent;
while(father != 0)
{
if(cur == T[father].L)
s.push('0');
else s.push('1');
cur = father;
father = T[father].parent;
}
while(!s.empty())
{
temp += s.top();
s.pop();
}
T[i].name = temp;
}
解码,一个循环遍历所有01,if 判断是否叶子节点
堆
用 树 的知识去管理 数组
i / 2 , i * 2 , i * 2 + 1
堆的重点:最大/小堆的生成
void maxHepify(int i)
{
int n;
int l = 2*i;
int r = 2*i+1;
if(l <= H && A[l] > A[i])
n = l;
else n = i;
if(r <= H && A[r] > A[n])
n = r;
if(n != i)
{
swap(A[n],A[i]);
maxHepify(n);
}
}
void Run()
{
for(int i = H/2;i >=1;i--)
{
maxHepify(i);
}
}
某一元素插入进堆中
//key为插入的值
H++;
A[H] = key;
int i = H;
if(A[Parent(i)] > key)
{
return;
}
else
{
while(i > 1 && A[Parent(i)] < A[i])
{
swap(A[i],A[Parent(i)]);
i = Parent(i);
}
}
去除最大堆最大的元素
A[1] = A[H];
H--;
maxHepify(1);
堆排序:在生成好堆后,不断将根节点取出
图
深搜DFS:逮住一条线往下搜
一个for遍历所有节点,进入DFS函数
DFS函数:对传进来的 节点 ,遍历与该节点连接的节点(未被搜过的)
广搜BFS:把周围的全搜了,再向外扩张
设置一个队列,将初始节点放进去
一个while(!queue.empty()),出队列,遍历出队列的节内未被搜过的节点,加入队列
加权图
最小生成树
prim算法:弄一个关于所有节点的 度的数组 ,进行一个循环:找出 度数组 中最小的度的点(未访问结束),遍历 该点
的度,并把 度数组 中的值更新,结束这个点(已访问)
while(true)
{
min = INF;
u = -1;
for(int i = 0;i < n;i++) //找出未被访问的最小路径的点
{
if(color[i] != BLACK && d[i] < min)
{
u = i;
min = d[i];
}
}
if(u == -1) break; //循环退出条件
color[u] = BLACK;
for(int i = 0;i < n;i++)
{
if(color[i] != BLACK && INF != M[i][u])
{
if(d[i] > M[i][u])
{
d[i] = M[i][u];
p[i] = u;
color[i] = GRAY;
}
}
}
}
Kruskal算法:并查集。将边的权值排序,然后进行一个n-1次的循环,从权值中选最小的那条,用并查集判断是否可行,并查集的根不同,就进行下一次循环,相同就抛弃该边,遍历下一条边
while(num < n-1) //n - 1 次外循环
{
if(merge(e[index].u,e[index].v))
{
num++;
sum += e[index].w;
}
index++;
}
并查集
int find(int x)
{
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
bool merge(int x,int y)
{
x = find(x);
y = find(y);
if(x == y)
return false;
fa[y] = x;
return true;
}
最短路径问题
Dijkstra算法:一个数组记录 V1 到其他顶点的 路径,先初始化,选出路径最小的节点,给予该节点一个“确定”,遍历该节点的出度,更新 V1数组,例如比较:V1-V3 : V1-V2-V3,进行循环,不断确定新的顶点
while(1)
{
u = -1;
minv = INF;
//取出目前出度最小的点(未被访问)
for(int i = 1;i <= n;i++)
{
if(Color[i] != BLACK && D[i] < minv)
{
minv = D[i];
u = i;
}
}
if(u == -1) break; //循环跳出条件
//遍历所有的点
for(int i = 1;i <= n;i++)
{
if(Color[i] != BLACK && D[i] > D[u] + Graph[u][i])
{
D[i] = D[u] + Graph[u][i];
P[i] = u;
}
}
}
SPFA:进行n-1次循环,每次循环对所有的边进行“松弛”
//u是起点数组,v在终点数组,w是权值
for(int i = 1;i <= n-1;i++) //循环节点数-1次
{
for(int j = 1;j <= m;j++) //遍历所有的边
{
if(D[v[i]] > D[u[i]] + w[i])
D[v[i]] = D[u[i]] + w[i];
}
}
SPFA用队列的形式,val是 i 到 j 的权值
while(!q.empty())
{
temp = q.front();
q.pop();
exist[temp] = false;
//遍历节点 temp 的所有连接点
for(int i = 0;i < g[temp].size();i++)
{
if(D[g[temp][i]] > D[temp] + val[temp][i])
{
D[g[temp][i]] = D[temp] + val[temp][i];
if(!exist[g[temp][i]])
{
q.push(g[temp][i]);
exist[g[temp][i]] = true;
}
}
}
}
Floyd算法:将一个二维数组 改进 为一个最短路径数组
//遍历所有节点,将二维数组g改造为最短路径数组
for(int k = 1;k <= n;k++)
for(int j = 1;j <= n;j++)
for(int i = 1;i <= n;i++)
if(g[i][j] > g[i][k] + g[k][j])
g[i][j] = g[i][k] + g[k][j];
//k一定要在最外层
拓扑排序
第一步:将所有入度为0的点放入栈中
//c[i]记录i节点出度的个数,r[i]记录i节点的入度
while(!s.empty())
{
temp = s.top();
s.pop();
for(int i = 1;i <= c[temp];i++)
{
r[g[temp][i]]--;
if(r[g[temp][i]] == 0)
s.push(g[temp][i]);
}
}