十种常见排序算法可以分为两大类:
冒泡排序(Bubble Sort)
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
选择排序(Selection Sort)
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
插入排序(Insertion Sort)
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
希尔排序(Shell Sort)
1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
归并排序(Merge Sort)
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
快速排序(Quick Sort)
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
堆排序(Heap Sort)
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
计数排序(Counting Sort)
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
桶排序(Bucket Sort)
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
基数排序(Radix Sort)
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前
常规答案
public static bool IsEven(int number)
{
return ((number % 2) == 0);
}
进阶答案
如奇数3和1位与,实际上是
00000000 00000000 00000000 00000011
& 00000000 00000000 00000000 00000001
---------------------------------------------
00000000 00000000 00000000 00000001
再比如偶数6和1位与,实际上是
00000000 00000000 00000000 00000110
& 00000000 00000000 00000000 00000001
---------------------------------------------
00000000 00000000 00000000 00000000
判断一个整数是奇数还是偶数
/** <summary>
/// 判断一个整数是否是偶数
/// </summary>
/// <param name="number">传入的整数</param>
/// <returns>如果是偶数,返回true,否则为false</returns>
public static bool IsEven(int number)
{
return ((number & 1) == 0);
}
/** <summary>
/// 判断一个整数是否是奇数
/// </summary>
/// <param name="number">传入的整数</param>
/// <returns>如果是奇数,返回true,否则为false</returns>
public static bool IsOdd(int number)
{
return !IsEven(number);
}
常规答案
判断一个整数是否是2的n次方
public static bool IsPower(int number)
{
if (number <= 0)
{
return false;
}
while (true)
{
if (number == 1)
{
return true;
}
//如果是奇数
if ((number & 1) == 1)
{
return false;
}
//右移一位
number >>= 1;
}
}
进阶答案
判断一个整数是否是2的n次方
public static bool IsPower(int number)
{
if (number <= 0)
{
return false;
}
if ((number & (number - 1)) == 0)
{
return true;
}
return false;
}
private static int GetCountGroupByOne(int data)
{
int count = 0;
if (data == 0)
{
}
else if (data > 0)
{
while (data > 0)
{
data &= (data - 1);
count++;
}
}
else
{
int minValue = -0x40000001;
while (data > minValue)
{
data &= (data - 1);
count++;
}
count++;
}
return count;
}
利用堆排序、小顶堆实现。
先拿10000个数建堆,然后一次添加剩余元素,如果大于堆顶的数(10000中最小的),将这个数替换堆顶,并调整结构使之仍然是一个最小堆,这样遍历完后,堆中的10000个数就是所需的最大的10000个。建堆时间复杂度是O(mlogm),算法的时间复杂度为O (nmlogm)(n为100,m为10000)。
优化的方法:分治。可以把所有10亿个数据分组存放,比如分别放在1000个文件中。这样处理就可以分别在每个文件的10^6个数据中找出最大的10000个数,合并到一起在再找出1000*10000中的最终的结果。
算法思路:假设目标值在闭区间[l, r]中,每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
int bsearch_1(int 1, int r)
{
While (l <r)
{
int mid = l +r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
int bsearch_2(int 1, int r)
{
While (l <r)
{
int mid = l + r +1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
1.如果不需要确定当前遍历到了哪一层
while queue 不空:
cur = queue.pop()
for 节点 in cur的所有相邻节点:
if 该节点有效且未访问过:
queue.push(该节点)
2.如果要确定当前遍历到了哪一层,BFS 模板如下。 这里增加了 level 表示当前遍历到二叉树中的哪一层了,也可以理解为在一个图中,现在已经走了多少步了。size 表示在当前遍历层有多少个元素,也就是队列中的元素数,我们把这些元素一次性遍历完,即把当前层的所有元素都向外走了一步。
level = 0
while queue 不空:
size = queue.size()
while (size --)
{
cur = queue.pop()
for 节点 in cur的所有相邻节点:
if 该节点有效且未访问过:
queue.push(该节点)
}
level++;
注意搜索的顺序;当搜到叶子节点(递归结束)时就回溯,回退一步看一步。
DFS从根节点开始搜索,并从根节点尽可能远地探索这些节点。使用堆栈数据结构来记住下一个节点访问。
类似于树的 先序遍历
static int Fn(int n) {
if (n <= 0) {
throw new ArgumentOutOfRangeException();
}
if (n == 1||n==2)
{
return 1;
}
return checked(Fn(n - 1) + Fn(n - 2)); // when n>46 memory will overflow
}
string a = new string("abc");
a = (a.ToUpper() +"123").Substring(0,2);
string b = new string(new char[] {'a','b','c'});
已知点P(x,y),以及直线上的两点A(x1,y1)、B(x2,y2),可以通过计算向量AP与向量AB的叉乘是否等于0来计算点P是否在直线AB上。
知识点:叉乘
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
已知点P(x,y),以及线段A(x1,y1),B(x2,y2)。
方法一
可以进行下面两部来判断点P是否在线段AB上:
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点是否在线段上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnSegment(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
//1.先通过向量的叉乘确定点是否在直线上
//2.在拍段点是否在指定线段的矩形范围内
if (IsPointOnLine(point,lineStart,lineEnd))
{
//点的x值大于最小,小于最大x值 以及y值大于最小,小于最大
if (point.x >= Mathf.Min(lineStart.x, lineEnd.x) && point.x <= Mathf.Max(lineStart.x, lineEnd.x) &&
point.y >= Mathf.Min(lineStart.y, lineEnd.y) && point.y <= Mathf.Max(lineStart.y, lineEnd.y))
return true;
}
return false;
}
// 数组
int[] array = new int[5]; // 声明一个可以容纳5个整数的数组
// 列表
List<int> list = new List<int>(); // 声明一个整数列表
List<T>.Add
添加元素超出当前容量时,会自动分配新的容量并复制元素。在Unity中,处理碰撞检测主要依赖于内置的物理引擎,它使用了一系列的算法来检测和处理物体之间的碰撞。Unity的碰撞检测主要基于边界盒(Bounding Boxes)或边界球(Bounding Spheres)的初步检测,随后是更精确的碰撞形状检测。以下是Unity中常见的碰撞检测相关概念和技术:
边界盒碰撞检测(Axis-Aligned Bounding Box, AABB):
. 边界球碰撞检测(Bounding Sphere):
网格碰撞器(Mesh Collider):
凸包碰撞器(Convex Hull Collider):
简单碰撞器(Primitive Colliders):
. 碰撞检测阶段:
. 碰撞事件:
OnCollisionEnter
、OnCollisionStay
和OnCollisionExit
,用于处理碰撞发生、持续和结束时的情况。触发器(Triggers):
OnTriggerEnter
、OnTriggerStay
和OnTriggerExit
。(y2 - y1) * t + y1
,其中 t
是从0到1之间的参数,表示当前位置与起点之间的相对位置。Lerp
函数用于线性插值,SmoothDamp
函数用于平滑地减小或增大数值,而SmoothFollow
函数则用于使一个对象平滑地跟随另一个对象。递归算法在Unity中可能会带来性能问题,主要原因在于递归算法需要大量的堆栈空间来存储每一层的函数调用,这可能会导致堆栈溢出。此外,如果递归算法没有正确地被优化,它可能会在处理大量数据时消耗大量的计算资源,从而导致性能问题。