插入排序从前往后遍历,默认前面都是已经排好序了,找到没排好序的那位数字往前查找,如果找到一个比它大的就往后挪一位,直到某一位不符合条件了,就把这一位后面的那个数的值置为该数。
//插入排序
void insert(vector<int> &v) {
int len = v.size();
for (int i=1; iint temp = v[i], j=i-1;
while (j>=0 && temp1]=v[j];
j--;
}
v[j+1]=temp;
}
}
shell排序是插排的一种改进,是通过找寻一个shell增量(默认是长度的两倍)开始,从该希尔增量往后不断进行插排,不过比较的不再是相邻的数,而是与该数相差一个希尔增量的前一个数,直到最左端为之。思路还是插排的思路,不过插排算法平均复杂度为O(n^2),而shell排序为O(nlogn),下面是代码实现;
//希尔排序
void shell(vector<int> &v) {
int len = v.size();
int gap=len/2,temp;
while (gap>0) {
for (int i=gap; iint j=i-gap;
while (j>=0 && temp2;
}
}
归并排序核心思想是分治法,每次都将数组分为两半,先排前面一半,再拍后面一半,最后将已经排好序的两部分再通过一个第三者数组过渡一下,排好序之再复制回原来的数组中,完成整体的排序。时间复杂度也是O(nlogn);
//归并排序
void merge(vector<int> &v, int l, int r) {
if (l>=r) return;
int mid=l+(r-l)/2;
merge(v, l, mid);
merge(v, mid+1, r);
vector<int> temp;
int m=l, n=mid+1;
for (int i=0; i1; i++) {
if (m>mid) temp.push_back(v[n++]);
else if(n>r) temp.push_back(v[m++]);
else if (v[m]else temp.push_back(v[n++]);
}
for (int i=0; i
快速排序实在太出名了,在这里也不再过多强调了。比如jdk中的Arrays.sort(T[])中就有采用快排(当然是在某一个阈值以上才采用,而且是双轴双向快排),快排的思想要深入理解了才能以不变应万变,比如理解单轴单向快排和单轴双向的区别,以及双轴双向快排的思想,以及面对链表如何进行快速排序?是值传递还是指针替换,都必须基于此。下面是几种几种实现方式:
//快速排序,基于算导上的标准写法
void quicksort1(vector<int> &v, int l, int r) {
if (l>=r) return;
int privot=v[r], index=l-1;
for (int i=l; iif (v[i] < privot) {
int temp = v[index+1];
v[++index] = v[i];
v[i] = temp;
}
}
v[r]=v[++index];
v[index]=privot;
quicksort1(v, l, index-1);
quicksort1(v, index+1, r);
}
//单轴双向快排
void quicksort(vector<int> &v, int left, int right)
{
if(left < right)//false则递归结束
{
int key=v[left];//基数赋值
int low = left;
int high = right;
while(low < high) //当low=high时,表示一轮分割结束
{
while(low < high && v[high] >= key)//胜利[low]为基数,从后向前与基数比较
{
high--;
}
swap(v[low],v[high]);
while(low < high && v[low] <= key)//胜利[high]为基数,从前向后与基数比较
{
low++;
}
swap(v[low],v[high]);
}
//分割后,对每一分段重复上述操作
quicksort(v, left,low-1);
quicksort(v, low+1,right);
}
}
以及如果面对是链表时候的快排序,下面是交换数值的方法,还有不改变node的val属性,而且整个node都进行迁移,那个算法比较复杂,这里并没实现;
private static void quickSort(Node head) {
quickSortMain(head, null);
}
private static void quickSortMain(Node head, Node tail) {
if (head != tail) {
Node mid = getPartition(head, tail);
quickSortMain(head, mid);
quickSortMain(mid.next, tail);
}
}
private static Node getPartition(Node head, Node tail) {
int key = head.val;
Node p = head, q = p.next;
while (q != tail) {
if (q.val < key) {
p = p.next;
int temp = q.val;
q.val = p.val;
p.val = temp;
}
q = q.next;
}
int temp = p.val;
p.val = head.val;
head.val = temp;
return p;
}
堆排序就是通过构建最大/小堆来进行排序,首先得理解最大/小堆的概念,然后知道如何构建,最大/小堆的调整算法,然后就是不断取值不断调整了。实现代码如下:
//堆排序
void heapsort(vector<int> &v) {
int len=v.size();
buildheap(v);
while(len>0) {
int temp=v[len-1];
v[len-1]=v[0];
v[0]=temp;
len--;
adjust(v, 0);
}
}
//建堆
void buildheap(vector<int> &v) {
for (int i=(v.size()-2)/2; i>=0; i--) {
adjust(v, i);
}
}
//调整算法
void adjust(vector<int> &v, int i) {
int maxIndex = i;
if (2*i < v.size() && v[2*i]2*i;
if (2*i+12*i+1]2*i+1;
if (maxIndex != i) {
int temp = v[maxIndex];
v[maxIndex]=v[i];
v[i]=temp;
adjust(v, maxIndex);
}
}
KMP算法是用来查找模式字符串pattern在目标字符串str中的位置(返回一个int数值)的一个高效算法,用过正则表达式表达式来匹配字符串的知道,通常要达到这个目的,str(长度n),pattern(长度m),最普遍的做法是从头开始查找,一旦不匹配str的下标重新返回到和pattern匹配的第一个位置,pattern同理,继续查找,总耗时O(n*m),但其实这时str可以保持不动,pattern字符只需要右移到某个位置即可,具体解释参考这几篇博客;如此一来的str字符串是保持一直往前匹配,pattern偶尔回溯,总耗时O(m+n),效率大大提高。
//获取next数组的函数
void getNext(vector<int> &next, string &p) {
int len = p.size(), k=-1;
next.push_back(-1);
for (int i=1; iwhile (k>-1 && p[k+1]!=p[i]) k=next[k];
if (p[k+1]==p[i]) k++;
next.push_back(k);
}
}
//KMP主函数
int kmp(string &str, string &p){
int i=0,j=0,slen=str.size(),plen=p.size();
vector<int> next;
getNext(next, p);
while (iif (str[i]==p[j]) {
i ++;
j ++;
} else {
if (next[j] == -1) {
j == 0;
i++;
} else
j = next[j];
}
if (j==plen) return i-j;
}
return -1;
}
其实核心还是理解了一个字符串的最长前缀和最长后缀相同是如何求算的,也即getNext()函数。
几乎在所有的图问题中,都会有两种方式的思路来解决存储图元素,然后基于此再进行下一步的操作。图元素包括点,边信息。
输入 n个顶点,m条边;
struct edge { int u, v; //两个顶点 int val; //边的长度 } int a[n][n]; //邻接矩阵 vector
v[n]; //邻接表
- 邻接矩阵:
将边u-v的权值放入a[u][v]中,a[u][v]为0,而如果u,v之间无直达边,设为无穷大;- 邻接表:
仅仅存储所有存在的边信息,存为一个edge对象,放入v[u]中,如果是无向边,也放入v[v]中。
main函数如下:
#define maxn 9999 struct edge { int u, v; int val; }; vector
v; void dfs(int,int, vector<int> &v); int a[100][100] = {maxn}; int main() { int n, m, u, v, val; scanf("%d%d", &n, &m); for (int i=1; i<=n; i++) { fill(a[i], a[i]+n+1, maxn); a[i][i]=0; } for (int i=0; i scanf("%d%d%d", &u, &v, &val); a[u][v]=val; a[v][u]=val; } for (int i=1; i<=n; i++) { for (int j=1; j<=n; j++) { printf("%04d ", a[i][j]); } printf("\n"); } vector<int> b; b.resize(n+1, 0); dfs(1, n, b); fill(b.begin(), b.end(), 0); bfs(1, n, b); }
深度优先遍历,其实就是从起点开始遍历,从遍历第一个邻接点开始,每遍历到一个点,就依次遍历它的所有邻接点,没有则返回。
void dfs(int s, int n, vector<int> &b) {
b[s]=1;
printf("深搜:遍历到了第%d个点\n", s);
for (int i=1; i<=n; i++) {
if (a[s][i]
广搜的思路就是搜索一个点,就先把其所有邻接点都遍历完,接着按顺序再进行邻接点自身的邻接点的遍历;
void bfs(int s, int n, vector<int> &b) {
queue<int> q;
q.push(s);
b[s]=1;
while (!q.empty()) {
int d = q.front();
q.pop();
printf("广搜:遍历到了第%d个点\n", d);
for (int i=1; i<=n; i++) {
if (a[d][i]1;
}
}
}
}
拓扑排序,就是找出一个所有顶点排布的顺序,使得按照在有向无环图中,不存在有任何一条边比如u->v,或者u-> …->v,有v排布顺序出现在u前面的情况出现;这个时候我们叫拓扑有序了。
拓扑排序的思想就是,先计算图中所有的点入度,然后找出入度为0的点,排在前面,每遍历一个入度为0的点,就删除该点,然后将他所有邻接点的入度减一处理。继续添加所有入度为0的点进队列中即可;
//拓扑排序时必须是有向无环图
void topology(int n) {
vector<int> degree;
degree.resize(n+1, 0);
//先初始化所有顶点的入度
for (int i=1; i<=n; i++) {
for (int j=1; j<=n; j++) {
if (a[i][j]0) degree[j]++;
}
}
queue<int> q;
//先找出所有的度为0的点加入队列
for (int i=1; i<=n; i++) {
if (!degree[i]) q.push(i);
}
while (!q.empty()) {
int t=q.front();
q.pop();
printf("拓扑排序:%d个顶点", t);
for (int i=1; i<=n; i++) {
if (a[t][i]0) {
degree[i]--;
if(!degree[i]) q.push(i);
}
}
}
}
拓扑排序还存在两种拓展(或变形);
最短路径算法,是用于解决一些实际问题而延申出来的。弗洛伊德最短路径算法,是用于求算,任意两点之间的最带路径距离的算法(路径不包含负权值),算法思路可以这样来概述:对于图中的任意两个点d1, d2之间的距离a[d1][d2],如果可以在经过第三方点d3进行过渡,使得满足a[d1][d3]+a[d3][d2] 因此要求出任意两点的最短距离,就得对每一个顶点都进行放缩处理。
void floyd(int n) {
for (int i=1; i<=n; i++) {
for (int u=1; u<=n; u++) {
for (int v=1; v<=n; v++) {
if (a[u][i]+a[i][v]
如果说floyd算法是用来求算任意两个点之间的最短路径算法的话,那么Dijkstra算法就是用来求算单源最短路径的所有最短路径,即起点s到图中任意一个点的最短距离。算法的核心思想是通过求算一个以s为起点的最短路径树,并不断扩展到所有的顶点去为止;维持一个距离数组,保存的都是每个点到起点的距离。每次都找数组中还没加入到最短路径树中最距离起点最短的那个点加入到最短距离树中,接着以它为松弛点,对dis数组中所有的顶点到起点的距离进行松弛。
void dijkstra(int s, int n) {
vector<int> dis;
dis.resize(n+1, maxn);
dis[s]=0;
vector<bool> b;
b.resize(n+1, false);
for (int i=1; i<=n; i++) {
int u=-1, min=maxn;
for (int j=1; j<=n; j++) {
if (!b[j] && dis[j]if (u==-1) printf("图并非连通...");
b[u]=true;
for (int j=1; j<=n; j++) {
if (dis[u]+a[u][j]
public class HuffmanTree {
public static class Node implements Comparable<Node>{
int weight;
String str;
String code;
Node left;
Node right;
public Node(String str, int weight) {
this.str = str;
this.weight = weight;
}
@Override
public int compareTo(Node o) {
return weight - o.weight;
}
}
public static Node createHuffmanTree(String[] strs, int[] weights) {
PriorityQueue queue = new PriorityQueue<>();
for (int i = 0; i < strs.length; i++) {
queue.offer(new Node(strs[i], weights[i]));
}
while (queue.size() >= 2) {
Node left = queue.poll();
Node right = queue.poll();
Node root = new Node("", left.weight + right.weight);
root.left = left;
root.right = right;
queue.offer(root);
}
return queue.peek();
}
public static Map getCode(Node root) {
Map map = new HashMap<>();
LinkedList deque = new LinkedList<>();
deque.addLast(root);
root.code = "";
Stack stack = new Stack<>();
while (!deque.isEmpty()) {
root = deque.removeFirst();
if (!root.str.equals("")) {
stack.push(root.str);
}
if (root.left != null) {
root.left.code = root.code + "0";
deque.addLast(root.left);
}
if (root.right != null) {
root.right.code = root.code + "1";
deque.addLast(root.right);
}
}
return map;
}
public static void main(String[] args) {
String[] strs = {"A", "B", "C", "D", "E"};
int[] weights = {5, 4, 3, 2, 1};
Node root = createHuffmanTree(strs, weights);
Map map = getCode(root);
for (String str : strs) {
System.out.println(str + " : " + map.get(str));
}
}
}