计算机中乘法指令比除法指令要快,如果涉及大量的计算,这也是一种优化,养成这个习惯也能提高程序的性能。例如:
f = num / 2; f = num * 0.5f;
这两个表达式的运算结果是一样的,但是使用乘法指令会比较快。
class Vectex {
public int Index;//代表该顶点的下标
public Vectex(int index) {
Index = index;
}
}
class Edge {
public Vectex From,To;
public int Weight;
public Edge(Vectex from, Vectex to,int weight) {
From = from;
To = to;
Weight = weight;
}
}
class Program
{
static void Main(string[] args)
{
//构建图
Vectex O = new Vectex(0);
Vectex A = new Vectex(1);
Vectex B = new Vectex(2);
Vectex C = new Vectex(3);
Vectex D = new Vectex(4);
Vectex[] vectexs = new Vectex[] { O, A, B, C, D };
Dictionary<Vectex, Edge[]> datas = new Dictionary<Vectex, Edge[]>();
Edge OA = new Edge(O, A, 2);
Edge OB = new Edge(O, B, 3);
Edge AC = new Edge(A, C, 7);
Edge AD = new Edge(A, D, -1);
Edge BD = new Edge(B, D, 1);
Edge DC = new Edge(D, C, 2);
datas.Add(O, new Edge[] { OA, OB });
datas.Add(A, new Edge[] { AC,AD });
datas.Add(B, new Edge[] { BD });
datas.Add(D, new Edge[] { DC });
//构建图 结束
int[] results = Ford(datas, vectexs);
foreach (var ii in results) {
Console.Write(ii + " ");
}
Console.ReadKey();
}
static int[] Ford(Dictionary<Vectex,Edge[]> datas, Vectex[] vectexs) {
int vNum = vectexs.Length;
int[] book = new int[vNum];
int[] dis = new int[vNum];
//初始化记录从原点到目标点的最短路径长度
for (int i = 0; i < vNum; i++) {
dis[i] = int.MaxValue;
}
dis[0] = 0;
Queue<Vectex> queue = new Queue<Vectex>();
queue.Enqueue(vectexs[0]);
book[0] = 1;
Vectex v;
while(queue.Count>0){
v = queue.Dequeue();
Edge[] edges;
datas.TryGetValue(v, out edges);
book[v.Index] = 0;
if (edges == null) {
continue;
}
foreach (var e in edges) {
if (dis[e.To.Index] > dis[v.Index] + e.Weight) {
//如果有需要,可以增加一个父节点记录数组,这样就可以通过目标点得到路径了
dis[e.To.Index] = dis[v.Index] + e.Weight;
if(book[e.To.Index == 0){
queue.Enqueue(e.To);
book[e.To.Index] = 1;
}
}
}
}
return dis;
}
}可以计算有负权值的图,这里计算了每一个点距离原点的最短距离。用一个队列来记录可能需要优化的顶点,还有一个book字典记录已经在队列中的顶点,防止同样的顶点再次进入队列,这样是没必要的。还有一个就是每个顶点距离原点的距离了。
首先初始化所有的值,队列,让原点进队。遍历队列的顶点,为每个顶点可到顶点寻找更短的路径,如果找到了,修改结果数组,把路径的结果顶点加入到队列中,因为它的值被改变了,可能会影响到它可到的顶点。当队列为空时,证明结果数组的路径都是最短路径了。
结果:0,2,3,3,1
static int n;
static void Main(string[] args)
{
Console.WriteLine("输入1-9的数字");
n = Convert.ToInt32( Console.ReadLine());
dfs();
Console.ReadKey();
}
static int[] book = new int[10];//用来标志数有没被用
static int[] result = new int[10];//记录结果
static void dfs(int step = 1) {
if (step > n) {
foreach (var val in result) {
if (val != 0) {
Console.Write(val + " ");
}
}
Console.WriteLine();
return;//退出口
}
for (int i = 1; i <= n; i++) {
if (book[i] == 0) {//这个数字没被用,则用它
result[step] = i;
book[i] = 1;
dfs(step+1);
//从上一步回来了,取回这个数才能尝试下一个数
book[i] = 0;
}
}
}深度优先搜索的思想,先遍历到最尾处出现一种情况了再返回
递归的方式实现,其实就是栈的方式,涉及深度优先搜索的采用栈来解决
这种计算遍历了所有情况
static void Main(string[] args)
{
int[] datas = new int[100000];
for (int j = 0; j < 100000; j++) {
datas[j] = r.Next(1, 10000);
}
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
sortArray(0, datas.Length - 1, datas);
stopwatch.Stop();
foreach (int data in datas) {
Console.WriteLine(data);
}
Console.WriteLine("是否排序好:" + isSort(datas));
Console.WriteLine("运行时间:" + stopwatch.ElapsedMilliseconds);
Console.ReadKey();
}
static void sortArray(int start,int end,int[] datas) {
if (start >= end) {
return;
}
int i = start;
int j = end;
//左边参考值 ,从小到大排序
int refer = datas[start];
while (i != j) {
//先搜索比参考值小的
while (datas[j] >= refer && j > i)
{
j--;
}
while (datas[i] <= refer && i < j) {
i++;
}
if (i < j) {
//交换
int temp = datas[i];
datas[i] = datas[j];
datas[j] = temp;
}
}
//把参考值放到中间相撞位置
datas[start] = datas[i];
datas[i] = refer;
sortArray(start, i - 1, datas);
sortArray(i + 1, end, datas);
}
static bool isSort(int[] datas) {
for (int i = 0; i < datas.Length -1; i++) {
if (datas[i] > datas[i + 1]) {
return false;
}
}
return true;
}思想就是,先定一个参考值(左边参考值或右边参考值),这个左右边参考值涉及到先搜索比参考值小的还是比参考值大的。例如,先定一个左边参考值,从右边搜索比参考值小的,再从数据左边开始搜索比参考值大的,然后交换这两个值,一直到这两个搜索撞到了一起。而这个撞在一起的值,就是那个先搜索的(比参考值小),让参考值归为到中间,因为选取参考值时是选取的左边位置,所以,直接交换中间相撞位置和参考值位置就行了。同理的,选取右边参考的话,就要先搜索左边的,先搜索比参考值大的。
模拟排序10W条随机数据用时:20ms,快得飞起啊,而且空间复杂度也低
static void Main(string[] args)
{
int[] datas = new int[100000];
Random r = new Random();
for (int i = 0; i < 100000; i++) {
datas[i] = r.Next(1, 10000);
}
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int[] newdatas = SortChoice(datas);
stopwatch.Stop();
foreach (int data in newdatas) {
Console.WriteLine(data);
}
Console.WriteLine("用时:" + stopwatch.ElapsedMilliseconds);
Console.ReadKey();
}
static int[] SortChoice(int[] datas)
{
int length = datas.Length;
int[] newArr = new int[datas.Length];
for (int x = 0; x < length; x++)
{
newArr[x] = datas[x];
}
int index = 0;
for (int i = 0; i < length; i++)
{
index = i;
for (int j = i + 1; j < length; j++)
{
if (newArr[j] < newArr[index])
{
index = j;
}
}
if (index != i) {
int temp = newArr[i];
newArr[i] = newArr[index];
newArr[index] = temp;
}
}
return newArr;
}原理其实跟冒泡排序是一样的,不同的是,选择排序减少了交换的次数,可以看出,代码中发现了更小的元素时,不是选择交换合适选择记录下来,最后的时候才判断需要需要交换再进行交换。
因为少了交换的步骤,效率会比冒泡排序的要快。
模拟排序10W条随机数据用时:16886ms
这个算法是归并算法的第二步根本所在,前面已经写过了
vector<int> mergeSortedArray(vector<int> &A, vector<int> &B) {
// write your code here
vector<int> res ;
int i = 0;
int j = 0;
//只需要比完一个数组中的元素
while(i<A.size()&&j<B.size()){
if(A[i]<B[j]){
res.push_back(A[i++]);
}else{
res.push_back(B[j++]);
}
}
//填充剩下的元素到后面
while(i<A.size()){
res.push_back(A[i++]);
}
while(j<B.size()){
res.push_back(B[j++]);
}
return res;
}这里是c++描述,合并需要考虑两个数组相差很大的情况,所有只需要比较一个数组中的所有元素
剩下的直接填充进去了,剩下的也必然比另外一个用完的大,所以,排序好了
static void Main(string[] args)
{
int n = 12;
Int64 sum = 1;
for (int i = 1; i <= n; i++) {
sum *= i;
}
Console.WriteLine("结果:" + sum);
Console.WriteLine(cal(n));
Console.ReadKey();
}
static int cal(int n) {
int temp = n;
int count = 0;
while (temp != 0) {
temp = temp / 5;
count += temp;
}
return count;
}在阶乘中,出现凡是出现5的都会与偶数化简的2相乘变成10,从而尾部就多了一个0。
例如25的阶乘,
1-4都没有5,这里0个,
5可以和2变成10,所以这里1个,
6-9,不可以化出5,
10=2*5,这里1个5,所以1个,
15=3*5,1个,
20=4*5,一个,
25=5*5,注意了,这里就有2个了,两个5可以和偶数组合成两个10相乘
所以25 = 2+1+1+1+1 = 6个0
可以化简成 25/5 + 25/5/5 = 6。
公式为:n/5 + n/5/5 + ... + 0
桶排序是一种快速排序的算法。也是一种采用分治思想的算法。主要思路就是将所有的数据根据大小的不同放到不同的桶当中,桶从小到大排列,对每个桶里面的元素进行排序后,再合并桶就完成排序了。
static void Main(string[] args)
{
//构造10w条随机的数据
Random r = new Random();
int[] datas = new int[100000];
for (int i = 0; i < 100000; i++) {
datas[i] = r.Next(1, 100000);
}
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<int> res = TongSort(datas);
stopwatch.Stop();
foreach (var data in res) {
Console.WriteLine(data);
}
Console.WriteLine("用时:" + stopwatch.ElapsedMilliseconds);
Console.ReadKey();
}
static List<int> TongSort(int[] datas) {
int length = datas.Length;
//根据数据长度计算桶的数量
int num = (int)Math.Log(length) + 1;
List<int>[] lists = new List<int>[num];//新建这么多个桶
//初始化桶
for (int init = 0; init < lists.Length; init++) {
lists[init] = new List<int>();
}
//确定区间,用最大和最小值
int min = 0, max = 0;
for (int i = 0; i < length; i++) {
if (min > datas[i])
{
min = datas[i];
}
else {
if (max < datas[i])
max = datas[i];
}
}
int section = (max - min) / num + 1;
int index = 0;
foreach (int data in datas) {
//确定元素,属于哪个桶的下标,添加到桶里
index = data / section;
lists[index].Add(data);
}
List<int> result = new List<int>();
//对所有桶分别排序,再合并
for (int j = 0; j < lists.Length; j++) {
lists[j].Sort();//不知道是不是快排
result.AddRange(lists[j]);
}
return result;
}对10w条随机数据的排序需要时间:9-11ms
跳跃表是一个对链表结构的扩展,快速查找到有序链表结点。对于链表,如果要查找某一个结点,这时候就要对链表进行一个一个的比较,这时候的时间复杂度就是O(n)。速度其实不算慢了,但是有没有更加快速的方法呢。就跟查询表数据那样,有几千万数据时,查询一条数据,需要一条一条来对比,这不就炸了吗?那么数据库是怎么解决的?就是用的索引了。在链表上建立一层一层的链表,存储对应索引或结点的引用,根据查找的数据,在第一层索引确定范围,然后第二层...到数据层链表,之后只需要沿着链表对比少量结点就能找到了。插入结点时,对节点做循环随机50%的几率升层作为索引,能上几层就几层。删除结点,删除相应对应的引用了的索引。
参考:什么是跳跃表?
static void Main(string[] args)
{
//随机10W个数据
int[] datas = new int[100000];
Random r = new Random();
for (int i = 0; i < 100000; i++) {
datas[i] = r.Next(1,10000);
}
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
sort(datas);//排序
stopwatch.Stop();
foreach (var data in datas) {
Console.WriteLine(data);
}
Console.WriteLine();
Console.WriteLine("用时:" + stopwatch.ElapsedMilliseconds);
Console.ReadKey();
}
static void sort(int[] datas) {
int length = datas.Length;
int[] temp = new int[length];
sort(datas, 0, length - 1, temp);
}
static void sort(int[] datas,int left,int right,int[] temp) {
if (left < right) {
int mid = (left + right)/2;
//拆分
sort(datas, left, mid, temp);
sort(datas, mid + 1, right, temp);
//合并,拆到不能拆就合并
merge(datas, left, mid, right, temp);
}
}
static void merge(int[] datas, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
//对比左右两边(两边都已经是有序的了,所以从左往右对比),排序(谁小先)填充到临时数组
while (i <= mid && j <= right) {
if (datas[i] < datas[j])
{
temp[t++] = datas[i++];
}
else {
temp[t++] = datas[j++];
}
}
//填充剩下了
while (i <= mid) {
temp[t++] = datas[i++];
}
while (j <= right) {
temp[t++] = datas[j++];
}
//将排序好的临时数据覆盖到原来的数组中
t = 0;
while (left <= right) {
datas[left++] = temp[t++];
}
}思想:将数据一直对半分组,分到组的数据都排序好为止(1个数据肯定是排序好的),再将相邻的组合并起来(边合并,边排序),合并成的组又是一个排序好的组。
时间复杂度:O(nlogn)
10W个随机数据排序用时:33ms
稳定而又快速的排序方法,参考:dreamcatcher-cx(归并排序)