常见数据结构与算法整理总结(下)

原文链接:https://www.jianshu.com/p/42f81846c0fb

这篇文章是常见数据结构与算法整理总结的下篇,上一篇主要是对常见的数据结构进行集中总结,这篇主要是总结一些常见的算法相关内容,文章中如有错误,欢迎指出。

一、概述
二、查找算法
三、排序算法
四、其它算法
五、常见算法题
六、总结

一、概述

以前看到这样一句话,语言只是工具,算法才是程序设计的灵魂。的确,算法在计算机科学中的地位真的很重要,在很多大公司的笔试面试中,算法掌握程度的考察都占据了很大一部分。不管是为了面试还是自身编程能力的提升,花时间去研究常见的算法还是很有必要的。下面是自己对于算法这部分的学习总结。

算法简介

算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。对于同一个问题的解决,可能会存在着不同的算法,为了衡量一个算法的优劣,提出了空间复杂度与时间复杂度这两个概念。

时间复杂度

一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数f(n),算法的时间度量记为 ** T(n) = O(f(n)) **,它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度。这里需要重点理解这个增长率。

举个例子,看下面3个代码:

1、{++x;}

2、for(i = 1; i <= n; i++) { ++x; }

3、for(j = 1; j <= n; j++)
for(j = 1; j <= n; j++)
{ ++x; }

上述含有 ++x 操作的语句的频度分别为1 、n 、n^2,

假设问题的规模扩大了n倍,3个代码的增长率分别是1 、n 、n^2

它们的时间复杂度分别为O(1)、O(n )、O(n^2)

空间复杂度

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。

二、查找算法

查找和排序是最基础也是最重要的两类算法,熟练地掌握这两类算法,并能对这些算法的性能进行分析很重要,这两类算法中主要包括二分查找、快速排序、归并排序等等。

顺序查找

顺序查找又称线性查找。它的过程为:从查找表的最后一个元素开始逐个与给定关键字比较,若某个记录的关键字和给定值比较相等,则查找成功,否则,若直至第一个记录,其关键字和给定值比较都不等,则表明表中没有所查记录查找不成功,它的缺点是效率低下。

二分查找

  • 简介

二分查找又称折半查找,对于有序表来说,它的优点是比较次数少,查找速度快,平均性能好。

二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x。

二分查找的时间复杂度为O(logn)

  • 实现
//给定有序查找表array 二分查找给定的值data
//查找成功返回下标 查找失败返回-1

static int funBinSearch(int[] array, int data) {

<span class="hljs-keyword">int</span> low = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> high = <span class="hljs-built_in">array</span>.length - <span class="hljs-number">1</span>;<span class="hljs-keyword">while</span> (low &lt;= high) {<span class="hljs-keyword">int</span> mid = (low + high) / <span class="hljs-number">2</span>;<span class="hljs-keyword">if</span> (data == <span class="hljs-built_in">array</span>[mid]) {<span class="hljs-keyword">return</span> mid;} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data &lt; <span class="hljs-built_in">array</span>[mid]) {high = mid - <span class="hljs-number">1</span>;} <span class="hljs-keyword">else</span> {low = mid + <span class="hljs-number">1</span>;}
}
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;

}

三、排序算法

排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。下面主要对一些常见的排序算法做介绍,并分析它们的时空复杂度。

常见排序算法

常见排序算法性能比较:

图片来自网络

上面这张表中有稳定性这一项,排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前和排序后他们的相对位置不发生变化。

下面从冒泡排序开始逐一介绍。

冒泡排序

  • 简介

冒泡排序的基本思想是:设排序序列的记录个数为n,进行n-1次遍历,每次遍历从开始位置依次往后比较前后相邻元素,这样较大的元素往后移,n-1次遍历结束后,序列有序。

例如,对序列(3,2,1,5)进行排序的过程是:共进行3次遍历,第1次遍历时先比较3和2,交换,继续比较3和1,交换,再比较3和5,不交换,这样第1次遍历结束,最大值5在最后的位置,得到序列(2,1,3,5)。第2次遍历时先比较2和1,交换,继续比较2和3,不交换,第2次遍历结束时次大值3在倒数第2的位置,得到序列(1,2,3,5),第3次遍历时,先比较1和2,不交换,得到最终有序序列(1,2,3,5)。

需要注意的是,如果在某次遍历中没有发生交换,那么就不必进行下次遍历,因为序列已经有序。

  • 实现
// 冒泡排序 注意 flag 的作用
static void funBubbleSort(int[] array) {
boolean flag = <span class="hljs-literal">true</span>;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">array</span>.length - <span class="hljs-number">1</span> &amp;&amp; flag; i++) {flag = <span class="hljs-literal">false</span>;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; <span class="hljs-built_in">array</span>.length - <span class="hljs-number">1</span> - i; j++) {<span class="hljs-keyword">if</span> (<span class="hljs-built_in">array</span>[j] &gt; <span class="hljs-built_in">array</span>[j + <span class="hljs-number">1</span>]) {<span class="hljs-keyword">int</span> temp = <span class="hljs-built_in">array</span>[j];<span class="hljs-built_in">array</span>[j] = <span class="hljs-built_in">array</span>[j + <span class="hljs-number">1</span>];<span class="hljs-built_in">array</span>[j + <span class="hljs-number">1</span>] = temp;flag = <span class="hljs-literal">true</span>;}}
}<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">array</span>.length; i++) {System.out.println(<span class="hljs-built_in">array</span>[i]);
}

}

  • 分析

最佳情况下冒泡排序只需一次遍历就能确定数组已经排好序,不需要进行下一次遍历,所以最佳情况下,时间复杂度为** O(n) **。

最坏情况下冒泡排序需要n-1次遍历,第一次遍历需要比较n-1次,第二次遍历需要n-2次,...,最后一次需要比较1次,最差情况下时间复杂度为** O(n^2) **。

简单选择排序

  • 简介

简单选择排序的思想是:设排序序列的记录个数为n,进行n-1次选择,每次在n-i+1(i = 1,2,...,n-1)个记录中选择关键字最小的记录作为有效序列中的第i个记录。

例如,排序序列(3,2,1,5)的过程是,进行3次选择,第1次选择在4个记录中选择最小的值为1,放在第1个位置,得到序列(1,3,2,5),第2次选择从位置1开始的3个元素中选择最小的值2放在第2个位置,得到有序序列(1,2,3,5),第3次选择因为最小的值3已经在第3个位置不需要操作,最后得到有序序列(1,2,3,5)。

  • 实现
static void funSelectionSort(int[] array) {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">array</span>.length - <span class="hljs-number">1</span>; i++) {<span class="hljs-keyword">int</span> mink = i;<span class="hljs-comment">// 每次从未排序数组中找到最小值的坐标</span><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = i + <span class="hljs-number">1</span>; j &lt; <span class="hljs-built_in">array</span>.length; j++) {<span class="hljs-keyword">if</span> (<span class="hljs-built_in">array</span>[j] &lt; <span class="hljs-built_in">array</span>[mink]) {mink = j;}}<span class="hljs-comment">// 将最小值放在最前面</span><span class="hljs-keyword">if</span> (mink != i) {<span class="hljs-keyword">int</span> temp = <span class="hljs-built_in">array</span>[mink];<span class="hljs-built_in">array</span>[mink] = <span class="hljs-built_in">array</span>[i];<span class="hljs-built_in">array</span>[i] = temp;}
}<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">array</span>.length; i++) {System.out.print(<span class="hljs-built_in">array</span>[i] + <span class="hljs-string">" "</span>);
}

}

  • 分析

简单选择排序过程中需要进行的比较次数与初始状态下待排序的记录序列的排列情况** 无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为 O(n^2) ,进行移动操作的时间复杂度为 O(n) 。总的时间复杂度为 O(n^2) **。

最好情况下,即待排序记录初始状态就已经是正序排列了,则不需要移动记录。最坏情况下,即待排序记录初始状态是按第一条记录最大,之后的记录从小到大顺序排列,则需要移动记录的次数最多为3(n-1)。

简单选择排序是不稳定排序。

直接插入排序

  • 简介

直接插入的思想是:是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。

例如,排序序列(3,2,1,5)的过程是,初始时有序序列为(3),然后从位置1开始,先访问到2,将2插入到3前面,得到有序序列(2,3),之后访问1,找到合适的插入位置后得到有序序列(1,2,3),最后访问5,得到最终有序序列(1,2,3,5).

  • 实现
static void funDInsertSort(int[] array) {
<span class="hljs-keyword">int</span> j;<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt; <span class="hljs-built_in">array</span>.length; i++) {<span class="hljs-keyword">int</span> temp = <span class="hljs-built_in">array</span>[i];j = i - <span class="hljs-number">1</span>;<span class="hljs-keyword">while</span> (j &gt; <span class="hljs-number">-1</span> &amp;&amp; temp &lt; <span class="hljs-built_in">array</span>[j]) {<span class="hljs-built_in">array</span>[j + <span class="hljs-number">1</span>] = <span class="hljs-built_in">array</span>[j];j--;}<span class="hljs-built_in">array</span>[j + <span class="hljs-number">1</span>] = temp;}<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">array</span>.length; i++) {System.out.print(<span class="hljs-built_in">array</span>[i] + <span class="hljs-string">" "</span>);
}

}

  • 分析

最好情况下,当待排序序列中记录已经有序时,则需要n-1次比较,不需要移动,时间复杂度为** O(n) 。最差情况下,当待排序序列中所有记录正好逆序时,则比较次数和移动次数都达到最大值,时间复杂度为 O(n^2) 。平均情况下,时间复杂度为 O(n^2) **。

希尔排序

希尔排序又称“缩小增量排序”,它是基于直接插入排序的以下两点性质而提出的一种改进:(1) 直接插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。(2) 直接插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。点击查看更多关于希尔排序的内容

归并排序

  • 简介

归并排序是分治法的一个典型应用,它的主要思想是:将待排序序列分为两部分,对每部分递归地应用归并排序,在两部分都排好序后进行合并。

例如,排序序列(3,2,8,6,7,9,1,5)的过程是,先将序列分为两部分,(3,2,8,6)和(7,9,1,5),然后对两部分分别应用归并排序,第1部分(3,2,8,6),第2部分(7,9,1,5),对两个部分分别进行归并排序,第1部分继续分为(3,2)和(8,6),(3,2)继续分为(3)和(2),(8,6)继续分为(8)和(6),之后进行合并得到(2,3),(6,8),再合并得到(2,3,6,8),第2部分进行归并排序得到(1,5,7,9),最后合并两部分得到(1,2,3,5,6,7,8,9)。

  • 实现
    //归并排序static void funMergeSort(int[] array) {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">array</span>.length &gt; <span class="hljs-number">1</span>) {<span class="hljs-keyword">int</span> length1 = <span class="hljs-built_in">array</span>.length / <span class="hljs-number">2</span>;<span class="hljs-keyword">int</span>[] array1 = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[length1];System.arraycopy(<span class="hljs-built_in">array</span>, <span class="hljs-number">0</span>, array1, <span class="hljs-number">0</span>, length1);funMergeSort(array1);<span class="hljs-keyword">int</span> length2 = <span class="hljs-built_in">array</span>.length - length1;<span class="hljs-keyword">int</span>[] array2 = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[length2];System.arraycopy(<span class="hljs-built_in">array</span>, length1, array2, <span class="hljs-number">0</span>, length2);funMergeSort(array2);<span class="hljs-keyword">int</span>[] datas = merge(array1, array2);System.arraycopy(datas, <span class="hljs-number">0</span>, <span class="hljs-built_in">array</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">array</span>.length);}}<span class="hljs-comment">//合并两个数组</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span>[] merge(<span class="hljs-keyword">int</span>[] list1, <span class="hljs-keyword">int</span>[] list2) {<span class="hljs-keyword">int</span>[] list3 = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[list1.length + list2.length];<span class="hljs-keyword">int</span> count1 = <span class="hljs-number">0</span>;<span class="hljs-keyword">int</span> count2 = <span class="hljs-number">0</span>;<span class="hljs-keyword">int</span> count3 = <span class="hljs-number">0</span>;<span class="hljs-keyword">while</span> (count1 &lt; list1.length &amp;&amp; count2 &lt; list2.length) {<span class="hljs-keyword">if</span> (list1[count1] &lt; list2[count2]) {list3[count3++] = list1[count1++];} <span class="hljs-keyword">else</span> {list3[count3++] = list2[count2++];}}<span class="hljs-keyword">while</span> (count1 &lt; list1.length) {list3[count3++] = list1[count1++];}<span class="hljs-keyword">while</span> (count2 &lt; list2.length) {list3[count3++] = list2[count2++];}<span class="hljs-keyword">return</span> list3;
}

  • 分析

归并排序的时间复杂度为O(nlogn),它是一种稳定的排序,java.util.Arrays类中的sort方法就是使用归并排序的变体来实现的。

快速排序

  • 简介

快速排序的主要思想是:在待排序的序列中选择一个称为主元的元素,将数组分为两部分,使得第一部分中的所有元素都小于或等于主元,而第二部分中的所有元素都大于主元,然后对两部分递归地应用快速排序算法。

  • 实现
// 快速排序
static void funQuickSort(int[] mdata, int start, int end) {if (end > start) {int pivotIndex = quickSortPartition(mdata, start, end);funQuickSort(mdata, start, pivotIndex - 1);funQuickSort(mdata, pivotIndex + 1, end);}
}

// 快速排序前的划分
static int quickSortPartition(int[] list, int first, int last) {

<span class="hljs-keyword">int</span> pivot = <span class="hljs-built_in">list</span>[first];
<span class="hljs-keyword">int</span> low = first + <span class="hljs-number">1</span>;
<span class="hljs-keyword">int</span> high = last;<span class="hljs-keyword">while</span> (high &gt; low) {<span class="hljs-keyword">while</span> (low &lt;= high &amp;&amp; <span class="hljs-built_in">list</span>[low] &lt;= pivot) {low++;}<span class="hljs-keyword">while</span> (low &lt;= high &amp;&amp; <span class="hljs-built_in">list</span>[high] &gt; pivot) {high--;}<span class="hljs-keyword">if</span> (high &gt; low) {<span class="hljs-keyword">int</span> temp = <span class="hljs-built_in">list</span>[high];<span class="hljs-built_in">list</span>[high] = <span class="hljs-built_in">list</span>[low];<span class="hljs-built_in">list</span>[low] = temp;}
}<span class="hljs-keyword">while</span> (high &gt; first &amp;&amp; <span class="hljs-built_in">list</span>[high] &gt;= pivot) {high--;
}<span class="hljs-keyword">if</span> (pivot &gt; <span class="hljs-built_in">list</span>[high]) {<span class="hljs-built_in">list</span>[first] = <span class="hljs-built_in">list</span>[high];<span class="hljs-built_in">list</span>[high] = pivot;<span class="hljs-keyword">return</span> high;
} <span class="hljs-keyword">else</span> {<span class="hljs-keyword">return</span> first;
}

}

  • 分析

在快速排序算法中,比较关键的一个部分是主元的选择。在最差情况下,划分由n个元素构成的数组需要进行n次比较和n次移动,因此划分需要的时间是O(n)。在最差情况下,每次主元会将数组划分为一个大的子数组和一个空数组,这个大的子数组的规模是在上次划分的子数组的规模上减1,这样在最差情况下算法需要(n-1)+(n-2)+...+1= ** O(n^2) **时间。

最佳情况下,每次主元将数组划分为规模大致相等的两部分,时间复杂度为** O(nlogn) **。

堆排序

  • 简介

在介绍堆排序之前首先需要了解堆的定义,n个关键字序列K1,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):(1) ki <= k(2i)且 ki <= k(2i+1) (1 ≤ i≤ n/2),当然,这是小根堆,大根堆则换成>=号。

如果将上面满足堆性质的序列看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有的非终端节点的值均不大于(或不小于)其左右孩子节点的值。

堆排序的主要思想是:给定一个待排序序列,首先经过一次调整,将序列构建成一个大顶堆,此时第一个元素是最大的元素,将其和序列的最后一个元素交换,然后对前n-1个元素调整为大顶堆,再将其第一个元素和末尾元素交换,这样最后即可得到有序序列。

  • 实现
//堆排序
public class TestHeapSort {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{<span class="hljs-keyword">int</span> arr[] = { <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">9</span> };heapsort(arr, <span class="hljs-number">6</span>);System.out.println(Arrays.toString(arr));
}<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">heapsort</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> n)</span> </span>{<span class="hljs-comment">// 先建大顶堆</span><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = n / <span class="hljs-number">2</span> - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {heapAdjust(arr, i, n);}<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; n - <span class="hljs-number">1</span>; i++) {swap(arr, <span class="hljs-number">0</span>, n - i - <span class="hljs-number">1</span>);heapAdjust(arr, <span class="hljs-number">0</span>, n - i - <span class="hljs-number">1</span>);}
}<span class="hljs-comment">// 交换两个数</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">swap</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> low, <span class="hljs-keyword">int</span> high)</span> </span>{<span class="hljs-keyword">int</span> temp = arr[low];arr[low] = arr[high];arr[high] = temp;
}<span class="hljs-comment">// 调整堆</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">heapAdjust</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arr[], <span class="hljs-keyword">int</span> index, <span class="hljs-keyword">int</span> n)</span> </span>{<span class="hljs-keyword">int</span> temp = arr[index];<span class="hljs-keyword">int</span> child = <span class="hljs-number">0</span>;<span class="hljs-keyword">while</span> (index * <span class="hljs-number">2</span> + <span class="hljs-number">1</span> &lt; n) {child = index * <span class="hljs-number">2</span> + <span class="hljs-number">1</span>;<span class="hljs-comment">// child为左右孩子中较大的那个</span><span class="hljs-keyword">if</span> (child != n - <span class="hljs-number">1</span> &amp;&amp; arr[child] &lt; arr[child + <span class="hljs-number">1</span>]) {child++;}<span class="hljs-comment">// 如果指定节点大于较大的孩子 不需要调整</span><span class="hljs-keyword">if</span> (temp &gt; arr[child]) {<span class="hljs-keyword">break</span>;} <span class="hljs-keyword">else</span> {<span class="hljs-comment">// 否则继续往下判断孩子的孩子 直到找到合适的位置</span>arr[index] = arr[child];index = child;}}arr[index] = temp;
}

}

  • 分析

由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。堆排序时间复杂度也为O(nlogn),空间复杂度为O(1)。它是不稳定的排序方法。与快排和归并排序相比,堆排序在最差情况下的时间复杂度优于快排,空间效率高于归并排序。

四、其它算法

在上面的篇幅中,主要是对查找和常见的几种排序算法作了介绍,这些内容都是基础的但是必须掌握的内容,尤其是二分查找、快排、堆排、归并排序这几个更是面试高频考察点。(这里不禁想起百度一面的时候让我写二分查找和堆排序,二分查找还行,然而堆排序当时一脸懵逼...)下面主要是介绍一些常见的其它算法。

递归

  • 简介

在平常解决一些编程或者做一些算法题的时候,经常会用到递归。程序调用自身的编程技巧称为递归。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。上面介绍的快速排序和归并排序都用到了递归的思想。

  • 经典例子

斐波那契数列,又称黄金分割数列、因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)。

//斐波那契数列 递归实现
static long funFib(long index) {
<span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>) {<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>) {<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
} <span class="hljs-keyword">else</span> {<span class="hljs-keyword">return</span> funFib(index - <span class="hljs-number">1</span>) + funFib(index - <span class="hljs-number">2</span>);
}

}

上面代码是斐波那契数列的递归实现,然而我们不难得到它的时间复杂度是O(2^n),递归有时候可以很方便地解决一些问题,但是它也会带来一些效率上的问题。下面的代码是求斐波那契数列的另一种方式,效率比递归方法的效率高。

static long funFib2(long index) {
<span class="hljs-keyword">long</span> f0 = <span class="hljs-number">0</span>;
<span class="hljs-keyword">long</span> f1 = <span class="hljs-number">1</span>;
<span class="hljs-keyword">long</span> f2 = <span class="hljs-number">1</span>;<span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>) {<span class="hljs-keyword">return</span> f0;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>) {<span class="hljs-keyword">return</span> f1;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (index == <span class="hljs-number">2</span>) {<span class="hljs-keyword">return</span> f2;
}<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">3</span>; i &lt;= index; i++) {f0 = f1;f1 = f2;f2 = f0 + f1;
}<span class="hljs-keyword">return</span> f2;

}

分治算法

分治算法的思想是将待解决的问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后合并这些子问题的解来建立最终的解。分治算法中关键地一步其实就是递归地求解子问题。关于分治算法的一个典型例子就是上面介绍的归并排序。查看更多关于分治算法的内容

动态规划

动态规划与分治方法相似,都是通过组合子问题的解来求解待解决的问题。但是,分治算法将问题划分为互不相交的子问题,递归地求解子问题,再将它们的解组合起来,而动态规划应用于子问题重叠的情况,即不同的子问题具有公共的子子问题。动态规划方法通常用来求解最优化问题。查看更多关于动态规划的内容

动态规划典型的一个例子是最长公共子序列问题。

常见的算法还有很多,比如贪心算法,回溯算法等等,这里都不再详细介绍,想要熟练掌握,还是要靠刷题,刷题,刷题,然后总结。

五、常见算法题

下面是一些常见的算法题汇总。

不使用临时变量交换两个数

static void funSwapTwo(int a, int b) {
a = a ^ b;
b = b ^ a;
a = a ^ b;System.out.println(a + <span class="hljs-string">" "</span> + b);

}

判断一个数是否为素数

static boolean funIsPrime(int m) {
<span class="hljs-keyword">boolean</span> flag = <span class="hljs-keyword">true</span>;<span class="hljs-keyword">if</span> (m == <span class="hljs-number">1</span>) {flag = <span class="hljs-keyword">false</span>;
} <span class="hljs-keyword">else</span> {<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">2</span>; i &lt;= Math.sqrt(m); i++) {<span class="hljs-keyword">if</span> (m % i == <span class="hljs-number">0</span>) {flag = <span class="hljs-keyword">false</span>;<span class="hljs-keyword">break</span>;}}
}<span class="hljs-keyword">return</span> flag;

}

其它算法题

1、15道使用频率极高的基础算法题
2、二叉树相关算法题
3、链表相关算法题
4、字符串相关算法问题

六、总结

以上就是自己对常见的算法相关内容的总结,算法虐我千百遍,我待算法如初恋,革命尚未成功,同志仍需刷题,加油。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2019蚂蚁金服 Java面试题目!涵盖现场3面真题

蚂蚁Java一面 二叉搜索树和平衡二叉树有什么关系&#xff0c;强平衡二叉树&#xff08;AVL树&#xff09;和弱平衡二叉树&#xff08;红黑树&#xff09;有什么区别 B树和B树的区别&#xff0c;为什么MySQL要使用B树 HashMap如何解决Hash冲突 epoll和poll的区别&#xff0c;…

数据结构--队列Queue--打印杨辉三角

杨辉三角大家很熟悉&#xff0c;不做介绍了&#xff0c;第n行的首末两元素均为1&#xff0c;中间n-2个元素由n-1行相邻两元素相加得到。 将第1行数据入队&#xff08;1&#xff09; -------队列表示&#xff08;队头&#xff0c;… 队尾&#xff09;------- 第2行数据1入队&am…

论文浅尝 | 基于常识知识图谱感知和图注意力机制的对话生成

OpenKG 祝各位读者中秋快乐!链接&#xff1a;http://coai.cs.tsinghua.edu.cn/hml/media/files/2018_commonsense_ZhouHao_3_TYVQ7Iq.pdf动机在以前的工作中&#xff0c;对话生成的信息源是文本与对话记录。但是这样一来&#xff0c;如果遇到 OOV 的词&#xff0c;模型往往难以…

7款优秀Vim插件帮你打造完美IDE

本文转载自公众号“夕小瑶的卖萌屋”&#xff0c;专业带逛互联网算法圈的神操作 -----》我是传送门 关注后&#xff0c;回复以下口令&#xff1a; 回复【789】 &#xff1a;领取深度学习全栈手册&#xff08;含NLP、CV海量综述、必刷论文解读&#xff09; 回复【入群】&#xf…

POJ 2259 team queue团队排队问题

题目链接&#xff1a; http://poj.org/problem?id2259 题目大意&#xff1a; 告诉你一堆人&#xff08;m个人是一组的&#xff0c;n个人是一组的。。。。&#xff09;&#xff1b;然后一个人来排队了&#xff0c;先看下有自己组的熟人吗&#xff1f;有的话直接排在自己组的人…

中文句法分析、标记规范及其应用

一、 Why句法分析 句法分析是比词高一个层级的语言单位&#xff0c;单个词之间通过中文语法规则进行组织&#xff0c;形成一个具有连贯语义的词语序列。为了揭示这种组织结构和规则&#xff0c;揭示出词语以及词语的成分&#xff0c;往往需要对其进行句法分析。就中文而言&…

蚂蚁三面题目(java开发岗):Java锁机制+JVM+线程池+事务+中间件

一面 1、HashMap底层原理&#xff1f;HashTable和ConcurrentHashMap他们之间的相同点和不同点&#xff1f; 2、由上题提到锁的问题 3、MySQL的表锁&行锁&乐观锁&悲观锁,各自的使用场景 4、Java线程锁有哪些&#xff0c;各自的优劣势 5、事务四大特性 6、事务的…

论文浅尝 | 当知识图谱遇上零样本学习——零样本学习综述

随着监督学习在机器学习领域取得的巨大发展&#xff0c;如何减少人工在样本方面的处理工作&#xff0c;以及如何使模型快速适应层出不穷的新样本&#xff0c;成为亟待解决的问题。零样本学习&#xff08;Zero-Shot Learning, ZSL&#xff09;的提出&#xff0c;则有效地解决了此…

ACL20 | 让笨重的BERT问答匹配模型变快!

一只小狐狸带你解锁炼丹术&NLP秘籍作者&#xff1a;曹庆庆&#xff08;Stony Brook University 在读PhD&#xff0c;关注Efficient NLP、QA方向&#xff0c;详见awk.ai&#xff09;背景BERT、XLNet、RoBERTa等基于Transformer[1]的预训练模型推出后&#xff0c;自然语言理解…

POJ 1581 优先队列 priority_queue -- 比赛胜者求解

题目链接&#xff1a;http://poj.org/problem?id1581 题目大意&#xff1a; 给定选手姓名&#xff0c;及答题提交次数&#xff08;提交正确前&#xff0c;错误一次罚20分&#xff09;&#xff0c;每题的做题时间罚分&#xff08;未正确作答的不罚分&#xff09;&#xff0c;…

热点事件发现、演化及时间线Timeline、故事线Storyline自动生成

一、热点事件概述 热点事件具有不可预测性&#xff0c;这决定了热点事件往往是一个“事后诸葛亮”的产物&#xff0c;一个热点事件从刚开始出现之时并没有带有成为热点的性质&#xff0c;当然&#xff0c;重大事故或者突发事件(如总统被杀、日本地震、印尼海啸、巴黎圣母院火灾…

计算机网络安全知识汇总

一、计算机网络面临的安全性威胁计算机网络上的通信面临以下的四种威胁&#xff1a; 截获——从网络上窃听他人的通信内容。 中断——有意中断他人在网络上的通信。 篡改——故意篡改网络上传送的报文。 伪造——伪造信息在网络上传送。截获信息的攻击称为被动攻击&#xff0c…

天猫研发Java团队(4面全题目):并发压测+Mina+事务+集群+秒杀架构

天猫高级Java一面 常见集合类的区别和适用场景 并发容器了解哪些&#xff1f; 如何判断链表是否有环 concurrentHashMap如何实现 集群服务器 如何application 共享 JAVA网络编程中&#xff1a;BIO、NIO、AIO的区别和联系 jvm内存模型jmm 知道的全讲讲 JAVA的垃圾回收&am…

回顾经典,Netflix的推荐系统架构

这篇文章我们回顾一篇经典博客&#xff0c;Netflix官方博客介绍的推荐系统架构&#xff0c;虽然文章发布已有六年&#xff0c; 但是现在回看起来我自己还是蛮惊讶的&#xff0c;因为Netflix的推荐系统架构居然到现在依然是主流。当然&#xff0c;框架中的诸多技术在不断的迭代更…

CNCC 技术论坛 | 知识图谱赋能数字经济

2018中国计算机大会&#xff08;CNCC2018&#xff09;将于10月25-27日在杭州国际博览中心&#xff08;G20会场&#xff09;举行&#xff0c;大会主题为「大数据推动数字经济」&#xff08;Big Data Drives the Digital Economy&#xff09;。10月15日前报名可享优惠&#xff0c…

计算机网络:我把所有计算机网络的基础都汇总到这里了!

原文链接&#xff1a;https://cloud.tencent.com/developer/article/1388529 前言计算机网络基础 该是程序猿需掌握的知识&#xff0c;但往往会被忽略今天&#xff0c;我将献上一份详细 & 清晰的计算机网络基础 学习指南&#xff0c;涵盖 TCP / UDP协议、Http协议、Socket等…

算法--递归--走台阶问题(2种递归+递归改循环)

文章目录递归&#xff1a;注意事项&#xff1a;问题1思路1.递归代码&#xff08;未考虑重复计算问题&#xff09;2.循环代码3.递归代码&#xff08;避免重复计算问题&#xff09;测试运行时间问题2递归&#xff1a; 一个问题可以分解成若干子问题&#xff0c;且求解思路一样&a…

最新2019 蚂蚁金服4面(Java)面试题

蚂蚁金服Java一面 1 自我介绍和项目 2 Java的内存分区 3 Java对象的回收方式&#xff0c;回收算法。 4 CMS和G1了解么&#xff0c;CMS解决什么问题&#xff0c;说一下回收的过程。 5 CMS回收停顿了几次&#xff0c;为什么要停顿两次。 6 Java栈什么时候会发生内存溢出&…

论文浅尝 | 基于表示学习的大规模知识库规则挖掘

链接&#xff1a;www.ict.griffith.edu.au/zhe/pub/OmranWW18.pdf动机传统的规则挖掘算法因计算量过大等原因无法应用在大规模KG上。为了解决这个问题&#xff0c;本文提出了一种新的规则挖掘模型RLvLR(Rule Learning via LearningRepresentation)&#xff0c;通过利用表示学习…

深度学习推荐系统中各类流行的Embedding方法

Embedding技术概览&#xff1a;对其它Embedding技术不熟悉&#xff0c;可以看我的上一篇文章&#xff1a;深度学习推荐系统中各类流行的Embedding方法&#xff08;上&#xff09;Graph Embedding简介Word2Vec和其衍生出的Item2Vec类模型是Embedding技术的基础性方法&#xff0c…