剑指offer 查找和排序的基本操作:查找排序算法大集合
重点
查找算法着重掌握:顺序查找、二分查找、哈希表查找、二叉排序树查找。
排序算法着重掌握:冒泡排序、插入排序、归并排序、快速排序。
顺序查找
算法说明
顺序查找适合于存储结构为顺序存储或链接存储的线性表。
算法思想
顺序查找也称为线形查找,属于无序查找算法。从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。
算法实现
int sequenceSearch(int a[], int value, int len)
{
int i;
for(i=0; i<len; i++)
if(a[i]==value)
return i;
return -1;
}
算法分析
查找成功时的平均查找长度为:(假设每个数据元素的概率相等) ASL = 1/n(1+2+3+…+n) = (n+1)/2 ;查找不成功时,需要n+1次比较,时间复杂度为O(n);所以,顺序查找的时间复杂度为O(n)。
二分查找
算法说明
元素必须是有序的,如果是无序的则要先进行排序操作。
算法思想
也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。注:折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。
算法实现
//二分查找,常规版
int binarySearch1(int a[], int value, int len)
{
int low, high, mid;
low = 0;
high = len-1;
while(low<=high)
{
mid = low+(high-low)/2; //防止溢出
if(a[mid]==value)
return mid;
if(a[mid]>value)
high = mid-1;
if(a[mid]<value)
low = mid+1;
}
return -1;
} //二分查找,递归版
int binarySearch2(int a[], int value, int low, int high)
{
int mid = low+(high-low)/2;
if(a[mid]==value)
return mid;
if(a[mid]>value)
return BinarySearch2(a, value, low, mid-1);
if(a[mid]<value)
return BinarySearch2(a, value, mid+1, high);
}
算法分析
最坏情况下,关键词比较次数为log2(n+1),故时间复杂度为O(log2n);
冒泡排序
算法说明
属于交换类排序,稳定排序
算法思想
比较相邻的两个数的大小,将最大的数放在右边,计数器i++;
继续重复操作1,直到a[n-2]和a[n-1]比较结束,数组a中最大的值已在a[n-1];
将进行排序的数组长度n减1,重复操作1和操作2,直到n为1,排序完毕。
算法实现
void bubbleSort(int* array, int length)
{
for (int i = 0; i < length - 1; ++i)
{
//bool is_Swap=false;
for (int j = 0; j < length - 1 - i; ++j)
{
if (array[j] > array[j + 1])
{
//is_Swap=true;
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
/*
交换还可使用如下方式
a = a + b;
b = a - b;
a = a - b;
交换还可使用如下方式
a=a^b;
b=b^a;
a=a^b;
*/
}
}
//if(is_Swap==false)
//return;
}
}
算法分析
平均时间复杂度:O(n^2)。最好的情况:如果待排序数据序列为正序,则一趟冒泡就可完成排序,排序的比较次数为n-1次,且没有移动,时间复杂度为O(n)。要实现O(n)的复杂度,代码里需要加一个标志位(Bool变量)。最坏的情况:如果待排序数据序列为逆序,则冒泡排序需要n-1次趟,每趟进行n-i次排序的比较和移动,即比较和移动次数均达到最大值:比较次数=n(n−1)/2=O(n^2),移动次数等于比较次数,因此最坏时间复杂度为O(n^2)。
插入排序
算法说明
属于插入类排序,稳定排序。
算法思想
从待排序的数组的第二个元素开始,将其与前面的数进行大小比较,寻找合适的位置并插入,直到全部元素都已插入。
算法实现
void insertSort(int* array,int length)
{
int i = 0, j = 0, temp = 0;
for (i = 1; i < length; ++i)
{
//如果该元素小于前面的元素,大于该元素的元素全部后移一位,
//直到找到该元素要插入的位置并插入之。
if (array[i] < array[i-1])
{
temp = array[i];
for (j = i-1; temp < array[j] && j >= 0 ; --j)
{
array[j+1] = array[j];
}
array[j + 1] = temp;
}
}
}
算法分析
平均时间复杂度:O(n^2)。最好的情况:当待排序记录已经有序,这时需要比较的次数为n-1=O(n)。最坏的情况:如果待排序记录为逆序,则最多的比较次数为n*(n-1)/2=O(n^2)
归并排序
算法说明
应用较广,稳定排序。
算法思想
归并排序是分治法的一个典型的应用,先使每个子序列有序,再使每个子序列间有序。将两个有序子序列合并成一个有序表,称为二路归并。 步骤:首先将有序数列一分二,二分四……直到每个区都只有一个数据,此时每个子序列都可看做有序序列。然后进行合并,每次合并都是有序序列在合并,
算法实现
void MergeArray(int* array, int first, int mid, int last, int* temp)
{
//将a[first...mid]和a[mid+1...last]合并
int i = first, j = mid + 1, k = 0;
int lengthA = mid+1, lengthB = last+1;
while (i < lengthA&&j < lengthB)
{
if (array[i] < array[j])
temp[k++] = array[i++];
else
temp[k++] = array[j++];
}
while (i < lengthA)
{
temp[k++] = array[i++];
}
while (j < lengthB)
{
temp[k++] = array[j++];
}
for (i = 0; i < k; ++i)
{
array[first + i] = temp[i];
}
} void MergeSort(int* array, int first, int last, int* temp)
{
if (first >= last)
return;
int mid = (first + last) / 2;
MergeSort(array, first, mid, temp);//左边有序
MergeSort(array, mid + 1, last, temp);//右边有序
MergeArray(array, first, mid, last, temp);//合并两个有序的子序列
}
算法分析
平均、最好、最坏的时间复杂度都为:O(n*log n)
可以这样理解:合并需要O(log n)步操作,每步将排好序的子序列合并需要O(n)的操作。那时间复杂度肯定是O(n*log n)。
快速排序
算法说明
在交换类排序算法中,快排是速度最快的。采用分治的思想,不稳定排序。
算法思想
从n个元素中选择一个元素作为分区的标准,一般选第一个元素;
把小于该元素的放在左边,把大于等于该元素的放在右边,中间就放该元素;
再分别对左右子序列重复操作1和2,直到每个子序列里只有一个元素,排序完毕。
算法实现
//版本1
void QuickSort(int* array,int low,int high)
{
if (low >= high)
return;
int left = low;
int right = high;
int key = array[left];//选择第一个元素作为区分元素,当然也可以选最后一个元素。
while (left != right)
{
while (left != right&&array[right] >= key)//从右往左,把小于key的元素放到key的左边
--right;
array[left] = array[right];
while (left != right&&array[left] <= key)//从左往右,把大于key的元素放到key的右边
++left;
array[right] = array[left];
}
array[left] = key;//此时left等于right //一分为二,分治思想,递归调用。
QuickSort(array, low, left - 1);
QuickSort(array, left + 1, high);
}
众所周知,Partition函数不管是在快速排序中,还是在找第K大这类问题中,都有很重要的地位,故而分开写,就有了版本2。
int Partition(int* array,int left,int right)
{
int key = array[left];
while (left != right)
{
while (left != right&&array[right] >= key)//从右往左,把小于key的元素放到key的左边
--right;
array[left] = array[right];
while (left != right&&array[left] <= key)//从左往右,把大于key的元素放到key的右边
++left;
array[right] = array[left];
}
array[left] = key;
return left;//返回区分函数
} //快排主函数
void quicksort(int* arr, int left, int right)
{
if(left< right)
{
int middle = mypartition(arr, left, right);
quicksort(arr, left, middle-1);
quicksort(arr, middle+1, right);
}
}
算法分析
平均时间复杂度:O(n*log n)。原因:快排是将数组一分为二到底,所以需要O(log n)次此操作,每次操作需要排序n次,所以,大多数情况下,时间复杂度都是O(n*log n)。最好的情况:是每趟排序结束后,每次划分使两个子文件的长度大致相等,时间复杂度为O(n*log n)。最坏的情况:是待排序元素已经排好序。第一趟经过n-1次比较后第一个元素保持位置不变,并得到一个n-1个元素的子序列;第二趟经过n-2次比较,将第二个元素定位在原来的位置上,并得到一个包括n-2个元素的子序列,依次类推,这样总的比较次数是:n(n-1)/2=O(n^2)。
剑指offer 查找和排序的基本操作:查找排序算法大集合的更多相关文章
- 剑指Offer - 九度1519 - 合并两个排序的链表
剑指Offer - 九度1519 - 合并两个排序的链表2013-11-30 22:04 题目描述: 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则.(hi ...
- 剑指Offer面试题:32.数字在排序数组中出现的次数
一.题目:数字在排序数组中出现的次数 题目:统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4. 二.解题思路 2 ...
- 《剑指offer》面试题53 - I. 在排序数组中查找数字 I
问题描述 统计一个数字在排序数组中出现的次数. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8 输出: 2 示例 2: 输入: nums = [5,7,7,8, ...
- 剑指Offer 二维数组中的查找
题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 思路法一: * 矩阵是 ...
- 剑指Offer——二维数组中的查找
题目描述: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 分析: 因为二维数组 ...
- 剑指offer—二维数组中的查找
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- 剑指offer之 二维数组的查找
package Problem3; public class Find { /* * 题目描述:二维数组中的查找 * 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下的顺序排 ...
- (java)剑指offer二维数组中的查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从 上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. pu ...
- 牛客网-剑指Offer 二维数组中的查找
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- 【剑指offer 面试题38】数字在排序数组中出现的次数
思路: 利用二分查找,分别查找待统计数字的头和尾的下标,最后做差加一即为结果. C++: #include <iostream> #include <vector> using ...
随机推荐
- Panda 交易所视点观察!区块链金融应用迎新规,哪些版块受影响?
Panda交易所获悉,近日央行下发推动<区块链技术规范应用的通知>(以下简称"通知")及<区块链技术金融应用评估规则>(以下简称"规则" ...
- Java并发编程的艺术(八)——锁相关
锁的作用 控制多个线程访问共享资源. 线程协作 Lock接口 特点 与synchronized类似的同步功能,只是需要显式地获取和释放锁.缺少隐式获取锁的便捷性. 拥有锁获取与释放的可操作性.可中断的 ...
- Editor.md解决跨域上传的问题
Editor.md解决跨域上传的问题 编辑 editormd\plugins\image-dialog\image-dialog.js 替换以下代码片段 if (settings.crossDomai ...
- I/O-外部设备
目录 输入设备 输出设备 显示器 阴极射线管(CRT)显示器 字符显示器 图形显示器 图像显示器 打印机 小结 外存储器 磁盘存储器 磁盘设备的组成 存储区域 硬盘存储器 磁盘的性能指标 磁盘地址 硬 ...
- 在Chrome、Firefox等高版本浏览器中实现低延迟播放海康、大华RTSP
一.背景 现在到处是摄像头的时代,随着带宽的不断提速和智能手机的普及催生出火热的网络直播行业,新冠病毒的大流行又使网络视频会议系统成为商务会议的必然选择,因此RTSP实时视频流播放及处理不再局限于安防 ...
- Java获取到异常信息进行保存(非Copy)
吐槽:不知道从什么时候开始,各大博客网站的文章开始各种复制粘贴,想好好找一个解决方法,搜索出来的博客基本上千篇一律,主要是能解决问题也还行,还解决不了问题这就恶心了.... 所以被迫自己写一篇文章,然 ...
- 关于easyii 无法退出登录的情况
问题描述:easyii 后台原先自己就写好了退出登录,如下图所示.点击了退出登录后,页面也会自动跳转到登录的页面.但是问题是,在浏览器点击返回的时候,还是依旧能进入到后台中,退出登录根本就没有起到作用 ...
- C4 模型 - 可视化架构设计
前言 世界上最难的两件事是: 1. 把我的思想放进你的脑袋 2. 把你的钱放进我的口袋 第二点我们不探讨,因为这是众所周知的,不信?过来试试:) 对于第一点,对我们程序员来说,其实也是我个人一直强调的 ...
- ubuntu20.04 LTS 更换国内163源、阿里源、清华源、中科大源
Ubuntu 20.04 是 Ubuntu 的第 8 个 LTS 版本,其重大更新和改进将在 2030 年前终止,计划于2020年 4 月 23 日发布. 国内有很多Ubuntu的镜像源,有阿里的.网 ...
- 手把手教你使用Python轻松搞定发邮件
前言 现在生活节奏加快,人们之间交流方式也有了天差地别,为了更加便捷的交流沟通,电子邮件产生了,众所周知,电子邮件其实就是客户端和服务器端发送接受数据一样,他有一个发信和一个收信的功能,电子邮件的通信 ...