算法图解读书笔记
作者:互联网
二分法查找
对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点。
适用于提高大列表查询速度,对于包含n个元素的列表,时间复杂度为:O(log2n)。
列表必须有序,并且当列表中存在多个相同元素时并非返回列表中第一次出现元素,不同实现算法获取到元素索引也不一定相同。
代码实现1
/// <summary> /// 递归实现元素查找 /// <para>集合长度不变,左右查找边界移动。</para> /// <para>已经做集合有序性检查,必须升序,否则返回-1</para> /// <para>集合中存在查找元素时返回索引,否则返回-1。</para> /// </summary> /// <param name="list">查找集合</param> /// <param name="t">待查找元素</param> /// <param name="left">查找左边界</param> /// <param name="right">查找右边界</param> /// <returns></returns> static int FindIndexByBisection(List<int> list, int t, int left, int right) { /*保证集合有序*/ if (list[left] > list[right]) return -1; /*集合中存在的元素一定在最小值与最大值之间*/ if (t < list[left] || t > list[right]) return -1; /*左右边界已经首次靠近,但是两边界所在元素均不等于待查找元素,直接返回*/ if (right - left == 1 && list[left] != t && list[right] != t) return -1; int midIndex = (left + right) / 2; int midValue = list[midIndex]; if (t < midValue) return FindIndexByBisection(list, t, left, midIndex); else if (t > midValue) return FindIndexByBisection(list, t, midIndex, right); else return midIndex; }
代码实现2
/// <summary> /// 递归实现元素查找 /// <para>集合长度变化,左右查找边界固定。</para> /// <para>已经做集合有序性检查,必须升序,否则返回-1。</para> /// <para>集合中存在查找元素时返回索引,否则返回-1。</para> /// </summary> /// <param name="list">查找集合</param> /// <param name="t">待查找元素</param> /// <param name="index">待查找元素索引</param> static void FindIndexByBisection(List<int> list, int t, ref int index) { /*初始值处理,防止调用方法中设置了错误默认值*/ int count = list.Count; int midIndex = count / 2; int midValue = list[midIndex]; /*找到元素立即返回*/ if (t == midValue) { index += 1; return; } /*未找到元素,且集合长度变为1返回-1*/ if (list.Count == 1) { index = -1; return; } /*未找到元素,集合长度大于1,但集合无序返回-1*/ if (list[count - 2] > list[count - 1]) { index = -1; return; } List<int> midList = list.Take(midIndex).ToList(); if (t > midValue) { index += midIndex;//元素比集合中间位置元素大时将中间元素索引累加上去 midList = list.Skip(midIndex).ToList(); } FindIndexByBisection(midList, t, ref index); }
算法效率
- 大O表示法
表示最糟糕情况下程序的运行时间。
- 常见大O运行时间
O(log n) 对数时间,二分查找。
O(n) 线性时间,简单查找。
O(n * log n) 快速排序算法。
O(n*n) 选择排序算法。
O(n!) 旅行商问题。
算法的速度指的并非时间,而是操作数的增速。
谈论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
大O表示法忽略常量是由于不同算法间效率相差几个数量级,而对于效率相近的算法常量有时候事关重大。
选择排序
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
时间复杂度O(n*n)。
代码实现1
/// <summary> /// 选择排序 /// <para>时间复杂度o(n^2)</para> /// </summary> /// <param name="list"></param> /// <returns></returns> static List<int> SelectionSort(List<int> list) { var result = new List<int>(); for (var i = 0; i < list.Count; i++) { var minValue = list[i]; for (var j = i; j < list.Count; j++) { if (list[j] < minValue) minValue = list[j]; } result.Add(minValue); } return result; }
递归
- 原则
如果使用循环,代码的性能可能更高;如果使用递归,程序可能更容易理解。如何选择要看什么对你来说更重要。
- 组成
- 基线条件
指什么条件下结束函数自我调用,避免无限循环;
- 递归条件
指函数调用自己。
- 基线条件
快速排序
分而治之
策略找出基线条件,条件尽可能简单。
不断将问题分解,直到符合基线条件。
快速排序原理
从数组中随机地选择一个元素作为基准值;
找出比基准值小以及比基准值大的元素,将数组分区;
小于基准值的元素构成的子数组;
基准值;
大于基准值的元素构成的子数组。
对两个子数组进行快速排序。
时间复杂度O(n log n)。
代码实现1
/// <summary> /// 快速排序 /// </summary> /// <param name="list"></param> /// <returns></returns> static List<int> QuickSort(List<int> list) { /*基准条件:当列表为空或者只存在一个元素时一定有序,返回列表*/ if (list?.Count <= 1) return list; var midIndex = list.Count / 2; var midValue = list[midIndex];//基准值 var lessList = new List<int>();//左列表 var greaterList = new List<int>();//右列表 for (int i = 1; i < list.Count; i++) { if (list[i] <= midValue) lessList.Add(list[i]); else greaterList.Add(list[i]); } lessList = QuickSort(lessList); greaterList = QuickSort(greaterList); var result = new List<int>(); result.AddRange(lessList); result.Add(midValue); result.AddRange(greaterList); return result; }
散列表
- 散列函数
总是将相同的输入映射到相同的索引;
将不同的输入映射到不同的索引;
知道数组大小,只返回有效索引。
- 填装因子
散列表包含的元素数/位置总数。
- 冲突
- 场景
给两个键分配的位置相同。
- 性能
数组中冲突位置存放一个链表,而将冲突键存到链表中。可以避免冲突性能较低。
保证较低的填装因子(<=1)。
- 场景
- 时间复杂度
O(1)。
标签:return,读书笔记,int,元素,list,midIndex,算法,查找,图解 来源: https://www.cnblogs.com/fuxuyang/p/11144333.html