Day42-45
今天是学习java的第42天了,因为后续的代码都很简单,再加上今天恰好有时间,所以直接一口气完成了四天的代码。
https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html
上面这个网站是旧金山大学的学习网站,下面所有排序的可视化的动画效果在上面都可以观看的到。当某种排序想不通的时候,不如去看看这种排序的动画,说不定会豁然开朗。其中可调动画的速度,也可手动step to step,非常好用。
Day 42 —— Hash
1. Description
我对哈希表的理解就是给数据做一个索引一样的东西。在代码实现上 ,则是让要存的数据里面的key取模一个数,把这个data存到那个取模的序号上去,到时候找这个数据也是按照这个取模的数值作为页码来取。
一股书籍目录的既视感。可以把我们要存的数据就是书里一页的内容,然后把这个内容按照一定的规则存到相应的页码。
哈希表刚开始学的时候觉得很绕,但是学通了之后感觉也很简单。emmm,我不知道怎么形容,我当时学这一章的时候最开始一直不懂哈希是啥,然后也一直晕乎乎的。但是当我学完之后我觉得这个好简单,反而不知道我当时为啥会卡住那么久了。
毕竟哈希表就是一个取模和存数的过程嘛。然后取模的规则可以自己定,老师的代码的规则是key取模length,然后让key对应的数据去data里面找对应它的坑位。如果那个坑位被之前来的占了,就找下一个坑位,如果下一个也被占了,就继续往下找,直到找到合适的坑位为止。
注意,老师这里的代码取模的值是length,坑位是一人一个,不会出现有数据没有坑位的情况,所以一直往下找也没事,实在到底了,根据哈希取模的规则又会转到data的头部去,所以不会出现数据没有坑位的情况。
有些哈希表的规则取模的数值是比数据的总是要多的,那样的话,可能会出现有一些数据找不到空着的坑位的情况。对于这种情况,我的常用做法是在那个坑位那里写一个链表,新来的数据也别往后找了,直接就在原来坑位那地方的下面新开一个节点,凑合一下。
2. Code
package datastructure.search;
public class DataArray {
class DataNode {
// The key.
int key;
// The data content
String content;
// The first constructor.
DataNode(int paraKey, String paraContent) {
key = paraKey;
content = paraContent;
}// Of the second constructor
// Overrides the toString.
public String toString() {
return "(" + key + ", " + content + ") ";
}// Of toString
} // Of class DataNode
// The data array.
DataNode[] data;
// The length of data array.
int length;
/**
*********************
* The first constructor.
*
* @param paraKeyArray The array of the keys.
* @param paraContentArray The array of contents.
*********************
*/
public DataArray(int[] paraKeyArray, String[] paraContentArray) {
length = paraKeyArray.length;
data = new DataNode[length];
for (int i = 0; i < length; i++) {
data[i] = new DataNode(paraKeyArray[i], paraContentArray[i]);
} // Of for i
}// Of the first constructor
// Overrides the toString,
public String toString() {
String resultString = "I am a data array with " + length + " items.\r\n";
for (int i = 0; i < length; i++) {
resultString += data[i] + " ";
} // Of for i
return resultString;
}// Of toString
public String sequentialSearch(int paraKey) {
data[0].key = paraKey;
// 这里虽然是一个简单的顺序查找,但我仍然要说一句,老师的代码真的好简单。
// 我在写的时候就无脑遍历,完全没有下面代码这样简洁。
int i; // 注意,这里不能把i写到for里面去,因为后面需要return data[i],所以i不能是局部变量。
for (i = length - 1; data[i].key != paraKey; i--) {
continue;
}//Of for i
return data[i].content;
} // Of sequentialSearch
// Test sequentialSearch.
public static void sequentialSearchTest() {
int[] tempUnsortedKeys = { -1, 5, 3, 6, 10, 7, 1, 9 };
String[] tempContents = { "null", "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
System.out.println("Search result of 10 is: " + tempDataArray.sequentialSearch(10));
System.out.println("Search result of 5 is: " + tempDataArray.sequentialSearch(5));
System.out.println("Search result of 4 is: " + tempDataArray.sequentialSearch(4));
}// Of sequentialSearchTest
/**
************
* 折半查找,相当于数学上的二分法。有一定的局限性,必须要数据有序排列。
*
* @param paraKey The given key.
* @return The content of the key.
************
*/
public String binarySearch(int paraKey) {
int tempLeft = 0;
int tempRight = length - 1;
int tempMiddle = (tempLeft + tempRight) / 2;
while (tempLeft <= tempRight) {
tempMiddle = (tempLeft + tempRight) / 2;
if (data[tempMiddle].key == paraKey) {
return data[tempMiddle].content;
} else if (data[tempMiddle].key <= paraKey) {
tempLeft = tempMiddle + 1;
} else {
tempRight = tempMiddle - 1;
}
} // Of while
// Not found.
return "null";
}// Of binarySearch
public static void binarySearchTest() {
int[] tempSortedKeys = { 1, 3, 5, 6, 7, 9, 10 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempSortedKeys, tempContents);
System.out.println(tempDataArray);
System.out.println("Search result of 10 is: " + tempDataArray.binarySearch(10));
System.out.println("Search result of 5 is: " + tempDataArray.binarySearch(5));
System.out.println("Search result of 4 is: " + tempDataArray.binarySearch(4));
}// Of binarySearchTest
/**
*********************
* The second constructor. 用哈希表的方法存储数据。
*
* @param paraKeyArray The array of the keys.
* @param paraContentArray The array of contents.
* @param paraLength The space for the Hash table.
*********************
*/
public DataArray(int[] paraKeyArray, String[] paraContentArray, int paraLength) {
// step 1. Initialize.
length = paraLength;
data = new DataNode[length];
for (int i = 0; i < length; i++) {
data[i] = null;
} // Of for i
// step 2. hash
int tempPosition;
for (int i = 0; i < paraKeyArray.length; i++) {
tempPosition = paraKeyArray[i] % length;
while (data[tempPosition] != null) {
tempPosition = (tempPosition + 1) % length;
System.out.println("Collision, move forward for key " + paraKeyArray[i]);
} // Of while
data[tempPosition] = new DataNode(paraKeyArray[i], paraContentArray[i]);
} // Of for i
}
/**
*********************
* Hash search. 上面的数据是用哈希表的方法存的,下面是要找到要存的数据,所以把上面的算法逆转过来即可。
*
* @param paraKey The given key.
* @return The content of the key.
*********************
*/
public String hashSearch(int paraKey) {
int tempPosition = paraKey % length;
while (data[tempPosition] != null) {
if (data[tempPosition].key == paraKey) {
return data[tempPosition].content;
} // Of if
System.out.println("Not this one for " + paraKey);
tempPosition = (tempPosition + 1) % length;
} // Of while
return "null";
}// Of hashSearch
/**
*********************
* Test the method.
*********************
*/
public static void hashSearchTest() {
int[] tempUnsortedKeys = { 16, 33, 38, 69, 57, 95, 86 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents, 19);
System.out.println(tempDataArray);
System.out.println("Search result of 95 is: " + tempDataArray.hashSearch(95));
System.out.println("Search result of 38 is: " + tempDataArray.hashSearch(38));
System.out.println("Search result of 57 is: " + tempDataArray.hashSearch(57));
System.out.println("Search result of 4 is: " + tempDataArray.hashSearch(4));
}// Of hashSearchTest
/**
************
* The entrance of the program.
*
* @param args Not used now.
************
*/
public static void main(String args[]) {
System.out.println("\r\n-------sequentialSearchTest-------");
sequentialSearchTest();
System.out.println("\r\n-------binarySearchTest-------");
binarySearchTest();
System.out.println("\r\n-------hashSearchTest-------");
hashSearchTest();
}// Of main
}
运行结果:
Day 43 —— Insertion Sort
1. Description
插入排序是一个很简单直观的排序方式,逻辑很简单,时间复杂度是O(n^2)。插入排序分为直接插入排序和折半插入排序,老师的代码就是直接插入排序。折半插入排序则是在直接插入排序的第二个循环中使用折半查找的方法,来找到tempKey
应该插入的地方,这样速度会快不少。但是总的来说也是直接插入排序的分支,所以下面直接插入排序就简称为插入排序。
插入排序,是在一组未排序的数据里面把头部的两个元素建立一个有序的小分支,然后把剩下未排序的元素插进这个小分支合适的地方,直到这组数据全部插入,这时就得到了一组有序的数据。
2. Code
/**
*********************
* Insertion sort.其中0号元素依然没有存有效数据。
*********************
*/
public void InsertionSort() {
DataNode tempNode;
int j;
for (int i = 2; i < length; i++) {
tempNode = data[i];
for (j = i - 1; data[j].key > tempNode.key; j--) {
data[j + 1] = data[j];
} // Of for j
data[j + 1] = tempNode; // 因为上面循环最后一句是j--,此时j已经运行到了我们要插入的那个位置的前一个坑位,所以这里是data[j + 1]。
System.out.println("Round " + (i - 1));
System.out.println(this);
} // Of for i
} // Of InsertionSort
/**
*********************
* Test the method.
*********************
*/
public static void InsertionSortTest() {
int[] tempUnsortedKeys = { -100, 5, 3, 6, 10, 7, 1, 9 };
String[] tempContents = { "null", "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
tempDataArray.InsertionSort();
System.out.println("Result\r\n" + tempDataArray);
}// Of insertionSortTest
运行结果:
Day 44 —— Shell Sort
希尔排序也很简单,但是怎么说呢,表述起来有些麻烦。最好的还是看代码或者看我发在开头的动画,那个更加直观。
1. Description
希尔排序在我看来是插入排序的一个补充。
从前面插入排序的代码中可以分析出:插入排序的时间复杂度是O(n^2),但是如果要排序的序列越接近正序,那么插入排序的时间复杂度就越低,可接近于O(n)。
所以就有了希尔排序这个东西,帮助插入排序先整理一遍数据,使其接近于有序,然后再由插入排序完成最后的收尾工作。
希尔排序的基本思想是:先将要排序的数据分成一个又一个组,先在这个组中排序。即设定一个增量,然后一个数字和这个数字的增量为一组。比如以5为增量,则Key=1、6、11、16、……为一组,2、7、12、17、……为另一组。这是第一遍循环,第二遍循环则减小这个增量的值,再来一遍,直到这个这个增量的值为1.
2. Code
package datastructure.search;
public class DataArray {
class DataNode {
// The key.
int key;
// The data content
String content;
// The first constructor.
DataNode(int paraKey, String paraContent) {
key = paraKey;
content = paraContent;
}// Of the second constructor
// Overrides the toString.
public String toString() {
return "(" + key + ", " + content + ") ";
}// Of toString
} // Of class DataNode
// The data array.
DataNode[] data;
// The length of data array.
int length;
/**
*********************
* The first constructor.
*
* @param paraKeyArray The array of the keys.
* @param paraContentArray The array of contents.
*********************
*/
public DataArray(int[] paraKeyArray, String[] paraContentArray) {
length = paraKeyArray.length;
data = new DataNode[length];
for (int i = 0; i < length; i++) {
data[i] = new DataNode(paraKeyArray[i], paraContentArray[i]);
} // Of for i
}// Of the first constructor
// Overrides the toString,
public String toString() {
String resultString = "I am a data array with " + length + " items.\r\n";
for (int i = 0; i < length; i++) {
resultString += data[i] + " ";
} // Of for i
return resultString;
}// Of toString
public String sequentialSearch(int paraKey) {
data[0].key = paraKey;
// 这里虽然是一个简单的顺序查找,但我仍然要说一句,老师的代码真的好简单。
// 我在写的时候就无脑遍历,完全没有下面代码这样简洁。
int i; // 注意,这里不能把i写到for里面去,因为后面需要return data[i],所以i不能是局部变量。
for (i = length - 1; data[i].key != paraKey; i--) {
continue;
}//Of for i
return data[i].content;
} // Of sequentialSearch
// Test sequentialSearch.
public static void sequentialSearchTest() {
int[] tempUnsortedKeys = { -1, 5, 3, 6, 10, 7, 1, 9 };
String[] tempContents = { "null", "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
System.out.println("Search result of 10 is: " + tempDataArray.sequentialSearch(10));
System.out.println("Search result of 5 is: " + tempDataArray.sequentialSearch(5));
System.out.println("Search result of 4 is: " + tempDataArray.sequentialSearch(4));
}// Of sequentialSearchTest
/**
************
* 折半查找,相当于数学上的二分法。有一定的局限性,必须要数据有序排列。
*
* @param paraKey The given key.
* @return The content of the key.
************
*/
public String binarySearch(int paraKey) {
int tempLeft = 0;
int tempRight = length - 1;
int tempMiddle = (tempLeft + tempRight) / 2;
while (tempLeft <= tempRight) {
tempMiddle = (tempLeft + tempRight) / 2;
if (data[tempMiddle].key == paraKey) {
return data[tempMiddle].content;
} else if (data[tempMiddle].key <= paraKey) {
tempLeft = tempMiddle + 1;
} else {
tempRight = tempMiddle - 1;
}
} // Of while
// Not found.
return "null";
}// Of binarySearch
public static void binarySearchTest() {
int[] tempSortedKeys = { 1, 3, 5, 6, 7, 9, 10 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempSortedKeys, tempContents);
System.out.println(tempDataArray);
System.out.println("Search result of 10 is: " + tempDataArray.binarySearch(10));
System.out.println("Search result of 5 is: " + tempDataArray.binarySearch(5));
System.out.println("Search result of 4 is: " + tempDataArray.binarySearch(4));
}// Of binarySearchTest
/**
*********************
* The second constructor. 用哈希表的方法存储数据。
*
* @param paraKeyArray The array of the keys.
* @param paraContentArray The array of contents.
* @param paraLength The space for the Hash table.
*********************
*/
public DataArray(int[] paraKeyArray, String[] paraContentArray, int paraLength) {
// step 1. Initialize.
length = paraLength;
data = new DataNode[length];
for (int i = 0; i < length; i++) {
data[i] = null;
} // Of for i
// step 2. hash
int tempPosition;
for (int i = 0; i < paraKeyArray.length; i++) {
tempPosition = paraKeyArray[i] % length;
while (data[tempPosition] != null) {
tempPosition = (tempPosition + 1) % length;
System.out.println("Collision, move forward for key " + paraKeyArray[i]);
} // Of while
data[tempPosition] = new DataNode(paraKeyArray[i], paraContentArray[i]);
} // Of for i
}
/**
*********************
* Hash search. 上面的数据是用哈希表的方法存的,下面是要找到要存的数据,所以把上面的算法逆转过来即可。
*
* @param paraKey The given key.
* @return The content of the key.
*********************
*/
public String hashSearch(int paraKey) {
int tempPosition = paraKey % length;
while (data[tempPosition] != null) {
if (data[tempPosition].key == paraKey) {
return data[tempPosition].content;
} // Of if
System.out.println("Not this one for " + paraKey);
tempPosition = (tempPosition + 1) % length;
} // Of while
return "null";
}// Of hashSearch
/**
*********************
* Test the method.
*********************
*/
public static void hashSearchTest() {
int[] tempUnsortedKeys = { 16, 33, 38, 69, 57, 95, 86 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents, 19);
System.out.println(tempDataArray);
System.out.println("Search result of 95 is: " + tempDataArray.hashSearch(95));
System.out.println("Search result of 38 is: " + tempDataArray.hashSearch(38));
System.out.println("Search result of 57 is: " + tempDataArray.hashSearch(57));
System.out.println("Search result of 4 is: " + tempDataArray.hashSearch(4));
}// Of hashSearchTest
/**
*********************
* Insertion sort.其中0号元素依然没有存有效数据。
*********************
*/
public void InsertionSort() {
DataNode tempNode;
int j;
for (int i = 2; i < length; i++) {
tempNode = data[i];
for (j = i - 1; data[j].key > tempNode.key; j--) {
data[j + 1] = data[j];
} // Of for j
data[j + 1] = tempNode; // 因为上面循环最后一句是j--,此时j已经运行到了我们要插入的那个位置的前一个坑位,所以这里是data[j + 1]。
System.out.println("Round " + (i - 1));
System.out.println(this);
} // Of for i
} // Of InsertionSort
/**
*********************
* Test the method.
*********************
*/
public static void InsertionSortTest() {
int[] tempUnsortedKeys = { -100, 5, 3, 6, 10, 7, 1, 9 };
String[] tempContents = { "null", "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
tempDataArray.InsertionSort();
System.out.println("Result\r\n" + tempDataArray);
}// Of insertionSortTest
/**
*********************
* Shell sort. We do not use sentries here because too many of them are needed.
*********************
*/
public void ShellSort() {
DataNode tempNode;
int[] tempJumpArray = { 5, 3, 1 };
int tempJump;
int p;
for (int i = 0; i < tempJumpArray.length; i++) {
tempJump = tempJumpArray[i];
for (int j = 0; j < tempJump; j++) {
for (int k = j + tempJump; k < length; k += tempJump) {
tempNode = data[k];
for (p = k - tempJump; p >= 0; p -= tempJump) {
if (data[p].key > tempNode.key) {
data[p + tempJump] = data[p];
} else {
break;
} // Of if
} // Of for p
// Insert.
data[p + tempJump] = tempNode;
} // Of for k
} // Of for j
System.out.println("Round " + i);
System.out.println(this);
} // Of for i
}// Of shellSort
/**
*********************
* Test the method.
*********************
*/
public static void ShellSortTest() {
int[] tempUnsortedKeys = { 5, 3, 6, 10, 7, 1, 9, 12, 8, 4 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while", "throw", "until", "do" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
tempDataArray.ShellSort();
System.out.println("Result\r\n" + tempDataArray);
}// Of shellSortTest
/**
************
* The entrance of the program.
*
* @param args Not used now.
************
*/
public static void main(String args[]) {
System.out.println("\r\n-------sequentialSearchTest-------");
sequentialSearchTest();
System.out.println("\r\n-------binarySearchTest-------");
binarySearchTest();
System.out.println("\r\n-------hashSearchTest-------");
hashSearchTest();
System.out.println("\r\n-------InsertionSortTest-------");
InsertionSortTest();
System.out.println("\r\n-------ShellSortTest-------");
ShellSortTest();
}// Of main
}
运行结果:
Day 45 Bubble Sort
1. Description
冒泡排序是其中最简单的排序,也是我最熟悉的一种排序。在3年多前,也就是高三毕业的那个暑假,还没进学校,就被我们学校计科院的几个学长骗去学C,写的第一个算法就是冒泡排序。我记得我当时C语言才刚学完while语句,for循环都没学,学长就让我写冒泡排序,说是最简单的排序。我当时想了好久,都不知道冒泡是怎么实现的,最后硬被我用while循环写出来。所以冒泡排序也是我最熟悉的一个排序了,当时就写过不知道多少遍。
冒泡排序,简单的说就是一个节点和它后面的那个节点比较,如果后面的比前面的小,那么它们就更换位置(这是升序的逻辑,如果要降序则相反)。总共有两层循环,确保每一个元素都会找到它应该在的位置。
老师的这个代码比我写的多了一个premature的判定,这个是我之前一直忽视的。写了这个之后可以节省一定的程序运行时间。
2. Code
public void BubbleSort() {
boolean tempSwapped;
DataNode tempNode;
for (int i = length - 1; i > 1; i--) {
tempSwapped = true;
for (int j = 0; j < i; j++) {
if (data[j].key > data[j + 1].key) {
tempNode = data[j];
data[j] = data[j + 1];
data[j + 1] = tempNode;
tempSwapped = false;
} // Of if
} // Of for j
// 这里如果temp还是tree,就说明上面一遍循环下来if语句就没触发过,这个说明数据全部是有序的。
if (tempSwapped) {
System.out.println("Premature");
break;
} // Of if
} // Of for i
} // Of BubbleSort
/**
*********************
* Test the method.
*********************
*/
public static void BubbleSortTest() {
int[] tempUnsortedKeys = { 1, 3, 6, 10, 7, 5, 9 };
String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);
System.out.println(tempDataArray);
tempDataArray.BubbleSort();
System.out.println("Result\r\n" + tempDataArray);
}// Of BubbleSortTest
运行结果: