《算法导论》第二章demo代码实现(Java版)

前言

表示晚上心里有些不宁静,所以就写一篇博客,来缓缓。囧

拜读《算法导论》这样的神作,当然要做一些练习啦。除了练习题与思考题那样的理论思考,也离不开编码的实践。

所以,后面每个章节,我都会尽力整理出章节中涉及的算法的Java代码实现。

二分查找

算法实现


package tech.jarry.learning.test.algorithms.binarysearch; public class BinarySearch { public static int binarySearch(int[] array, int target) {
return binarySearch(array, target, 0, array.length - 1);
} // 二分查找,要求输入的线性表必须是顺序的
public static int binarySearch(int[] array, int target, int startIndex, int endIndex) { if (endIndex > startIndex) {
int middleIndex = (startIndex + endIndex) / 2; if (target < array[middleIndex]){
return binarySearch(array, target, startIndex, middleIndex);
} else if (target > array[middleIndex]) {
return binarySearch(array, target, middleIndex + 1, endIndex);
} else if (target == array[middleIndex]) {
return middleIndex;
}
} return -1;
}
}

算法测试


package tech.jarry.learning.test.algorithms.binarysearch; public class BinarySearchTest { public static void main(String[] args) {
int[] testArray = new int[] {3, 5, 7, 2, 4, 1, 5, 0, 9, 18 ,12}; System.out.println(BinarySearch.binarySearch(testArray, 100));
}
}

结果输出

-1

这表示没有找到目标数据,可以将测试类中的target修改为其他数字。

冒泡排序

算法实现


package tech.jarry.learning.test.algorithms.bubblesort; public class BubbleSort { public static int[] bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++){
for (int j = i; j < array.length; j++) {
if (array[j] < array[i]) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
return array;
}
}

算法测试


package tech.jarry.learning.test.algorithms.bubblesort; import java.util.Arrays; public class BubbleSortTest {
public static void main(String[] args) { int[] testArray = new int[] {3, 5, 7, 2, 4, 1, 5, 0, 9, 18 ,12}; System.out.println(Arrays.toString(BubbleSort.bubbleSort(testArray)));
}
}

结果输出

[0, 1, 2, 3, 4, 5, 5, 7, 9, 12, 18]

插入排序

算法实现


package tech.jarry.learning.test.algorithms.insertsort; import java.util.Arrays; public class InsertionSort { public static int[]insertSort(int[] originArray) {
// 从数组的第二个元素开始进行比较(总不能第一个元素和第一个元素自己比较大小吧)
for (int j = 1; j < originArray.length; j++) {
// 获取当前元素的值
int key = originArray[j];
// 获取前一个元素的下标
int i = j - 1;
// 将key值前移(即将遇到的每个大于key的元素后移)
while (i >= 0 && originArray[i] > key) {
originArray[i + 1] = originArray[i];
i = i - 1;
}
// 直到遇到originArray[i] <= key,才对i+1进行赋值(而i+1元素之前已经后移复制了,即i+2位置保存了i+1位置的值)
originArray[i + 1] =key;
} return originArray;
} public static int[] insertSortProWithBinarySearch(int[] array) {
// 如果数据组织形式是数组,那么即使采用二分查找优化,底层的数组元素移动,依旧会导致最终的时间复杂度变为n^2,而不是期待的n*lgn
return null;
}
}

算法测试


package tech.jarry.learning.test.algorithms.insertsort; import java.util.Arrays; public class InsertionSortTest {
// test
public static void main(String[] args) {
int[] originArray = new int[]{9, 6, 4, 5, 8};
int[] resultArray = InsertionSort.insertSort(originArray);
System.out.println(Arrays.toString(resultArray));
}
}

结果输出

[4, 5, 6, 8, 9]

归并排序

算法实现

这个代码的实现,可能内容比较多。一方面是由于方法提取(提取哨兵创建的操作),另一方面是由于增加了练习题中提到的无哨兵归并排序的实现(在mergeSort方法中,可以自由选择是否使用哨兵)。


package tech.jarry.learning.test.algorithms.mergesort; import java.util.Arrays; /**
* 归并排序
*/
public class MergeSort { public static int[] mergeSort(int[] array) {
return mergeSort(array, 0, array.length - 1);
} public static int[] mergeSort(int[] array, int startIndex, int endIndex) {
if (startIndex < endIndex) {
int middleIndex = startIndex + (endIndex - startIndex) / 2;
array = mergeSort(array, startIndex, middleIndex);
array = mergeSort(array, middleIndex + 1, endIndex); // 使用哨兵,进行合并
// return merge(array, startIndex, middleIndex, endIndex);
// 不适用哨兵,进行合并
return noSentinelMerge(array, startIndex, middleIndex, endIndex);
} // 如果startIndex = endIndex,表示array只有一个元素
return array;
} private static int[] merge(int[] array, int startIndex, int middleIndex, int endIndex) {
int[] sentinelLeftArray = createSentinelArray(array, startIndex, middleIndex);
int[] sentinelRightArray = createSentinelArray(array, middleIndex + 1, endIndex); for (int i = 0, m = 0, n = 0; i < endIndex - startIndex + 1; i++) {
if (sentinelLeftArray[m] < sentinelRightArray[n]) {
// 这里千万别忘了startIndex,因为不同分支的起点不同
array[startIndex + i] = sentinelLeftArray[m++];
} else {
array[startIndex + i] = sentinelRightArray[n++];
}
// 不用考虑两个Integer.MAX_VALUE,因为最后两个数组分别剩下的元素必然是这两个哨兵元素
}
return array;
} private static int[] createSentinelArray(int[] array, int startIndex, int endIndex) {
int length = endIndex - startIndex + 1;
int[] sentinelArray = new int[length + 1];
for (int i = 0; i < length; i++) {
sentinelArray[i] = array[startIndex + i];
}
sentinelArray[endIndex - startIndex + 1] = Integer.MAX_VALUE;
return sentinelArray;
} // p.22_practise2.3-2 在不使用哨兵的前提下,进行归并排序的合并操作
private static int[] noSentinelMerge(int[] array, int startIndex, int middleIndex, int endIndex) {
int[] leftArray = createNonSentinelBranchArray(array, startIndex, middleIndex);
int[] rightArray = createNonSentinelBranchArray(array, middleIndex + 1, endIndex); for (int i = 0, m = 0, n = 0; i < endIndex - startIndex + 1; i++) {
if (leftArray[m] < rightArray[n]) {
array[startIndex + i] = leftArray[m++];
if (m == leftArray.length) {
// 将rightArray剩下的元素全部复制到array对应位置中
array = branchArray2Array(array, startIndex + i + 1, rightArray, n);
break;
}
} else {
array[startIndex + i] = rightArray[n++];
if (n == rightArray.length) {
// 将leftArray剩下的元素全部复制到array对应位置中
array = branchArray2Array(array, startIndex + i + 1, leftArray, m);
break;
}
}
// 不用考虑两个Integer.MAX_VALUE,因为最后两个数组分别剩下的元素必然是这两个哨兵元素
}
return array;
} private static int[] createNonSentinelBranchArray(int[] array, int startIndex, int endIndex) {
int length = endIndex - startIndex + 1;
int[] branchArray = new int[length];
for (int i = 0; i < length; i++) {
branchArray[i] = array[startIndex + i];
}
return branchArray;
} private static int[] branchArray2Array(int[] array, int targetIndex, int[] branchArray, int startIndex) {
while (startIndex < branchArray.length) {
array[targetIndex++] = branchArray[startIndex++];
}
return array;
} // 由于一些情况(如内存空间不足),数据可以直接保存到硬盘中。而不是保存在内存的数组中
private static void merge2Disk(int[] array, int startIndex, int middleIndex, int endIndex){
int[] sentinelLeftArray = createSentinelArray(array, startIndex, middleIndex);
int[] sentinelRightArray = createSentinelArray(array, middleIndex + 1, endIndex); for (int i = 0, m = 0, n = 0; i < endIndex - startIndex + 1; i++) {
if (sentinelLeftArray[m] < sentinelRightArray[n]) {
Disk.store(sentinelLeftArray[m++]);
} else {
Disk.store(sentinelRightArray[n++]);
}
// 不用考虑两个Integer.MAX_VALUE,因为最后两个数组分别剩下的元素必然是这两个哨兵元素
}
} // test_creatreSentinelArray
public static void main(String[] args) {
int[] testArray = new int[] {3, 5, 7, 2, 4, 1, 5, 0};
System.out.println(Arrays.toString(createSentinelArray(testArray, 0 , 2)));
}
}

补充:上述代码涉及的Disk类

之所以在归并排序中增加这个硬盘操作,是因为我做这道题想起来很久之前遇到的一道面试题。就是问如何用1G的空间,去排序8G的数据。答案就是采用归并排序(当时原理说出来了,但是白板没写好)。


package tech.jarry.learning.test.algorithms.mergesort; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; /**
* 模仿真实硬盘,进行数据的存储与打印数据
*/
public class Disk { private static List<Integer> diskIntegerInstance = new ArrayList<>(); public static void store(int element) {
diskIntegerInstance.add(element);
} public static void printAll() {
Iterator<Integer> integerIterator = diskIntegerInstance.iterator();
while (integerIterator.hasNext()) {
System.out.println(integerIterator.next());
}
} }

算法测试


package tech.jarry.learning.test.algorithms.mergesort; import java.util.Arrays; public class MergeSortTest { public static void main(String[] args) {
int[] testArray = new int[] {3, 5, 7, 2, 4, 1, 5, 0, 9, 18 ,12};
// 3, 5, 7, 2, 4, 1, 5, 0, 9, 18 ,12
// correct result: [0, 1, 2, 3, 4, 5, 5, 7, 9, 12, 18]
int[] resultArray = MergeSort.mergeSort(testArray);
System.out.println(Arrays.toString(resultArray));
}
}

结果输出

[0, 1, 2, 3, 4, 5, 5, 7, 9, 12, 18]

确定两数之和为固定值

这道题在leetcode中是存在的,之前的博客也有对应的解析。甚至leetcode还有求三数之和为确定值的题目。

算法实现


package tech.jarry.learning.ch2.algorithms.twosum; import tech.jarry.learning.ch2.algorithms.mergesort.MergeSort; public class TwoSum { // 题目中只要求实现确定是否存在,而无需返回对应index。否则,需要注意剔除相同index的问题,并修改binarySearch的返回值
public static boolean twoSum(int[] array, int target) {
array = MergeSort.mergeSort(array); for (int i = 0; i < array.length; i++) {
int branchTarget = target - array[i]; // 二分查找的时间复杂度为lgn
if (binarySearch(array, branchTarget)) {
return true;
}
}
return false;
} private static boolean binarySearch(int[] array, int target) {
return binarySearch(array, target, 0, array.length - 1);
} private static boolean binarySearch(int[] array, int target, int startIndex, int endIndex) {
if (endIndex > startIndex) {
int middleIndex = (endIndex + startIndex) / 2;
if (target < array[middleIndex]) {
return binarySearch(array, target, startIndex, middleIndex);
} else if (target > array[middleIndex]) {
return binarySearch(array, target, middleIndex + 1, endIndex);
} else if (target == array[middleIndex]) {
return true;
}
} return false;
}
}

算法测试


package tech.jarry.learning.test.algorithms.twosum; public class TwoSumTest { public static void main(String[] args) {
int[] testArray = new int[] {3, 5, 7, 2, 4, 1, 5, 0, 9, 18 ,12};
int target = 80;
System.out.println(TwoSum.twoSum(testArray, target));
}
}

结果输出

false

由于书中的demo只要求输出存在与否,而leetcode类似的题目,则要求返回两个元素的index。感兴趣的朋友,可以看我之前写的有关leetcode求两数之和解法的博客。

总结

其实,这章算法的demo还是比较容易实现的。更多的是找找实现算法的感觉吧。

如果代码存在什么问题,或者你们存在什么疑惑,可以私信或@我。

愿与诸君共进步。

《算法导论》第二章demo代码实现(Java版)的更多相关文章

  1. 算法导论 第一章and第二章(python)

    算法导论 第一章 算法     输入--(算法)-->输出   解决的问题     识别DNA(排序,最长公共子序列,) # 确定一部分用法     互联网快速访问索引     电子商务(数值算 ...

  2. 算法<初级> - 第二章 队列、栈、哈希表相关问题

    算法 - 第二章 数据结构 题目一 用数组实现大小固定的队列和栈(一面题) 数组实现大小固定栈 /*** * size是对头索引(initSize是固定大小) 也是当前栈大小 * size=下个进队i ...

  3. 《算法》第二章部分程序 part 4

    ▶ 书中第二章部分程序,加上自己补充的代码,包括优先队列和索引优先队列 ● 优先队列 package package01; import java.util.Comparator; import ja ...

  4. 《算法》第二章部分程序 part 2

    ▶ 书中第二章部分程序,加上自己补充的代码,包括若干种归并排序,以及利用归并排序计算数组逆序数 ● 归并排序 package package01; import java.util.Comparato ...

  5. 《算法》第二章部分程序 part 1

    ▶ 书中第二章部分程序,加上自己补充的代码,包括插入排序,选择排序,Shell 排序 ● 插入排序 package package01; import java.util.Comparator; im ...

  6. 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组

    第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...

  7. MIT算法导论——第二讲.Solving Recurrence

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  8. 《算法》第二章部分程序 part 5

    ▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...

  9. 《算法》第二章部分程序 part 3

    ▶ 书中第二章部分程序,加上自己补充的代码,包括各种优化的快排 package package01; import edu.princeton.cs.algs4.In; import edu.prin ...

随机推荐

  1. 序言vue.js介绍

    vue.js :渐进式JavaScript框架 vue.js 优点 1.体积小 例如:压缩后 33k; 2.更高的运行效率 基于虚拟dom,一种可以预先通过JavaScript进行各种计算,把最终的D ...

  2. java架构之路(多线程)JUC并发编程之Semaphore信号量、CountDownLatch、CyclicBarrier栅栏、Executors线程池

    上期回顾: 上次博客我们主要说了我们juc并发包下面的ReetrantLock的一些简单使用和底层的原理,是如何实现公平锁.非公平锁的.内部的双向链表到底是什么意思,prev和next到底是什么,为什 ...

  3. Uva1014:Remember the Word

    Neal is very curious about combinatorial problems, and now here comes a problem about words. Knowing ...

  4. 【Java并发基础】安全性、活跃性与性能问题

    前言 Java的多线程是一把双刃剑,使用好它可以使我们的程序更高效,但是出现并发问题时,我们的程序将会变得非常糟糕.并发编程中需要注意三方面的问题,分别是安全性.活跃性和性能问题. 安全性问题 我们经 ...

  5. java中5种异步转同步方法

    先来说一下对异步和同步的理解: 同步调用:调用方在调用过程中,持续等待返回结果. 异步调用:调用方在调用过程中,不直接等待返回结果,而是执行其他任务,结果返回形式通常为回调函数. 其实,两者的区别还是 ...

  6. 编译游戏库allegro

    一个allegro依赖了大概十个库,还得自己一个个的去编译,然后复制粘贴 主要从两个网页学到的 第一个网页里有绝大多数的依赖库的编译方法 http://wiki.allegro.cc/index.ph ...

  7. C#反射与特性(九):全网最全-解析反射

    目录 1,判断类型 1.1 类和委托 1.2 值类型 1.3 接口 1.4 数组 2, 类型成员 2.1 类 2.2 委托 2.3 接口 [微信平台,此文仅授权<NCC 开源社区>订阅号发 ...

  8. UML--> plantUML安装

    plantUML安装 因为基于intellid idea,所以第一步自行安装. setting->plugins 搜索plantUML 安装完成后,重启idea 会有如下显示 安装Graphvi ...

  9. RoBERTa模型总结

    RoBERTa模型总结 前言 ​ RoBERTa是在论文<RoBERTa: A Robustly Optimized BERT Pretraining Approach>中被提出的.此方法 ...

  10. Docker基础内容之端口映射

    随机映射 docker run -d -P training/webapp python app.py # -P会随机映射一个 49000~49900 的端口到内部容器开放的网络端口 映射所有接口地址 ...