本文作者: HuaShan
本文链接: http://dhsmp.com/2018/02/26/data_struct/
内存溢出主要是指,要装的数据要超过容器本身了,这是则会导致溢出。类似与生活中的水桶、杯子等,容量是有限的,所以不能进行无限的填装。像Java中的OOM,就属于内存溢出。因为Java中GC的存在,所以并不会发生内存泄露。
上一章用到C语言制造链表,对于链表中结点的删除操作,会有一个free()函数的调用。free()函数的作用是释放内存空间。所谓的释放内存空间,是指把对内存地址的操作权限移交给操作系统。如果没有做free()这个操作,删除该结点时,会导致该结点在已经无法找到了,但却还拥有控制权限。操作系统在分配内存时,会认为当前内存地址有人还在使用,就不会分配给其他的应用程序,从而会导致内存空间,越用越少。
所谓稳定性是指,如果序列中两个数据元素相等,即r[i]==r[j],排序前r[i]在r[j]前,排序后r[i]仍然在r[j]前,则这个排序算法是稳定的,反之是不稳定的。
其基本思想是从待排序的无序区中找出最小的元素,将该元素与该区中的第一个元素交换位置。该排序算法是不稳定的。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 上午9:27:32
* @remark
* 选择排序:每一次在无序的序列中选出关键字最小的记录,依次存放在已排好序的记录序列最后。
* 原始 数据:5 4 9 87 1 2 14 26
* 第一趟 1 5 4 9 87 2 14 26
* 第二趟 1 2 5 4 9 87 14 26
* 第三趟 1 2 4 5 9 87 14 26
* 第四趟 1 2 4 5 9 87 14 26
* 第五趟 1 2 4 5 9 87 14 26
* 第六趟 1 2 4 5 9 14 87 26
* 第七趟 1 2 4 5 9 14 26 87
*/
public class ChooseSort {
public static void main(String[] args) {
//统计运行次数
int count = 0;
int[] array = { 5, 4, 9, 87, 1, 2, 14, 26 };
chooseSort(array, count);
}
/**
* 选择排序
* @param array
* @param count
*/
public static void chooseSort(int[] array, int count) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if(array[j] < array[i]){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
count++;
}
}
System.out.println(Arrays.toString(array) + "--->ChooseSort:" + count);
}
}
其核心思想是把待排序区中的第一个元素拿出来,把位置给空出来。然后用该元素,跟有序区中的元素进行比较。假设是从小到大排序,则比该元素大的,往后移动一次。依次进行即可。把该元素插入到不能移动的元素后边的空间中。该排序算法是稳定的。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 上午9:57:20
* @remark 插入排序:1、元素拿出来。2、符合条件的元素后移
*/
public class InsertSort {
public static void main(String[] args) {
// 统计运行次数
int count = 0;
int[] array = { 5, 4, 9, 87, 1, 2, 14, 26 };
insertSort(array, count);
}
/**
* 插入排序
* @param array
* @param count
*/
public static void insertSort(int[] array, int count) {
int pos = 0;
for (int i = 2; i < array.length; i++) {
// 待插入的位置
pos = i;
// 元素拿出来
int temp = array[i];
for (int j = i - 1; j >= 0 && array[j] > temp; j--) {
// 符合条件的元素后移
array[j + 1] = array[j];
// 需要插入的位置
pos = j;
count++;
}
array[pos] = temp;
}
System.out.println(Arrays.toString(array) + "--->InsertSort:" + count);
}
}
其基本思想是通过相邻元素之间的比较和交换,来完成排序。有向上冒泡和向下下沉。该排序算法是稳定的。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 上午10:41:21
* @remark 冒泡排序:相邻元素之间的比较交换。一种是向上冒泡,一种是向下下沉
*/
public class BubbleSort {
public static void main(String[] args) {
// 统计运行次数
int count = 0;
int[] array1 = { 5, 4, 9, 87, 1, 2, 14, 26 };
int[] array2 = { 5, 4, 9, 87, 1, 2, 14, 26 };
int[] array3 = { 5, 4, 9, 87, 1, 2, 14, 26 };
int[] array4 = { 5, 4, 9, 87, 1, 2, 14, 26 };
count = bubbleSort1(array1, 0);
System.out.println(Arrays.toString(array1) + "--->bubbleSort1:" + count);
count = bubbleSort2(array2, 0);
System.out.println(Arrays.toString(array2) + "--->bubbleSort2:" + count);
count = bubbleSort3(array3, 0);
System.out.println(Arrays.toString(array3) + "--->bubbleSort3:" + count);
count = bubbleSort4(array4, 0);
System.out.println(Arrays.toString(array4) + "--->bubbleSort4:" + count);
int[] array5 = { 1, 2, 4, 3, 5, 6, 7, 8 };
count = bubbleSort4(array5, 0);
System.out
.println(Arrays.toString(array5) + "--->bubbleSort4:" + count);
}
/**
* 向上冒泡
*
* @param array
* @param count
*/
public static int bubbleSort1(int[] array, int count) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = 1; j < array.length - i; j++) {
if (array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
count++;
}
}
return count;
}
/**
* 向下下沉
*
* @param array
* @param count
*/
public static int bubbleSort2(int[] array, int count) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = array.length - 1; j > i; j--) {
if (array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
count++;
}
}
return count;
}
/**
* 向上冒泡,优化
*
* @param array
* @param count
*/
public static int bubbleSort3(int[] array, int count) {
// 标记,true表示:元素还没有排好序
boolean flag = true;
for (int i = 0; i < array.length - 1 && flag; i++) {
// 认为已经排好序
flag = false;
for (int j = 1; j < array.length - i; j++) {
if (array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
// 元素执行了交换,则说明数据还没有排好序
flag = true;
}
count++;
}
}
return count;
}
/**
* 向下下沉:优化
*
* @param array
* @param count
*/
public static int bubbleSort4(int[] array, int count) {
// 标记,true表示:元素还没有排好序
boolean flag = true;
for (int i = 0; i < array.length - 1 && flag; i++) {
// 认为已经排好序
flag = false;
for (int j = array.length - 1; j > i; j--) {
if (array[j] < array[j - 1]) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
// 元素执行了交换,则说明数据还没有排好序
flag = true;
}
count++;
}
}
return count;
}
}
其核心思想是对数据进行分组,然后再对分组后的数据进行插入排序算法。该排序算法是不稳定的。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 下午3:23:48
* @remark 希尔排序:对数据进行分组,然后再进行插入排序算法
*/
public class ShellSort {
public static void main(String[] args) {
// 统计运行次数
int count = 0;
int[] array = { 5, 4, 9, 87, 1, 2, 14, 26 };
shellSort(array, count);
}
/**
* 希尔排序
* @param array
* @param count
*/
public static void shellSort(int[] array, int count) {
int gap = array.length;
int pos = 0;
do {
//业界统一实验 平均最好间隔情况
gap = gap / 3 + 1;
for (int i = gap; i < array.length; i += gap) {
// 待插入的位置
pos = i;
// 元素拿出来
int temp = array[i];
for (int j = i - gap; j >= 0 && array[j] > temp; j -= gap) {
// 符合条件的元素后移
array[j + gap] = array[j];
// 需要插入的位置
pos = j;
count++;
}
array[pos] = temp;
}
} while (gap > 1);
System.out.println(Arrays.toString(array) + "--->ShellSort:" + count);
}
}
其基本思想是从待排序序列中找一个基准数,以基准数进行左右分区,其中一部分的所有数据都比另外一部分所以数据小,基准数排在两个分区序列中间。然后再按此方法对这两部分数据分别进行快速排序,递归进行,达到变成有序序列。该排序算法是不稳定的。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 下午4:30:56
* @remark 快速排序:用一个PV值,跟PV值进行比较来分区,小的放左边,大的放右边。对分过区的,再找PV值进行分区,直到分区只有一个元素
*/
public class QuickSort {
public static void main(String[] args) {
int[] array = { 5, 4, 9, 87, 1, 2, 14, 26 };
quickSort(array);
System.out.println(Arrays.toString(array) + "--->QuickSort");
}
/**
* @param array
*/
public static void quickSort(int[] array) {
qSort(array, 0, array.length - 1);
}
/**
*
* @param array
* @param low
* @param hight
*/
public static void qSort(int[] array, int low, int hight) {
if (low < hight) {
int pivot = partition(array, low, hight);
// 对左区间序列排序
qSort(array, low, pivot - 1);
// 对右区间序列排序
qSort(array, pivot + 1, hight);
}
}
/**
* 分区
* @param array
* @param low
* @param hight
* @return
*/
public static int partition(int[] array, int low, int hight) {
// 用分区后的第一个元素作为PV值
int pv = array[low];
while (low < hight) {
while (low < hight && array[hight] >= pv) {
// 比基准数大,本来就在右边,所以hight向前移动
hight--;
}
if (low < hight) {
array[low] = array[hight];
low++;
}
while (low < hight && array[low] <= pv) {
// 比基准数小,本来就在左边,所以low向前移动
low++;
}
if (low < hight) {
array[hight] = array[low];
hight--;
}
}
//把拿出来的PV数,放回到low的位置
array[low] = pv;
return low;
}
}
其基本思想是使用辅助空间,首先是对待排序序列进行拆分,以递归的方式,拆成只剩下一个的时候,再两两进行归并操作,按指定的条件进行排序。再执行最后一次归并时,如果最后一次比较后,某个序列中有多余的元素,会直接插入到辅助空间后面。
package datastructure.part.sort;
import java.util.Arrays;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月27日 下午7:24:42
* @remark 归并排序:两两归并
*/
public class MergeSort {
public static void main(String[] args) {
int[] array = { 5, 4, 9, 87, 1, 2, 14, 26 };
mergeSort(array);
System.out.println(Arrays.toString(array) + "--->MergeSort");
}
/**
* @param array
*/
public static void mergeSort(int[] array) {
mSort(array, array, 0, array.length - 1, array.length);
}
/**
* 归并排序
* @param src:源数组
* @param des:目标数组
* @param low:最低位
* @param hight:最高位
* @param max:最大值
*/
public static void mSort(int[] src, int[] des, int low, int hight, int max) {
if (low == hight) {
// 只有一个元素,不需要归并
des[low] = src[low];
} else {
// 如果多个元素,进行两路划分
int[] space = new int[max];
int mid = (low + hight) / 2;
mSort(src, space, low, mid, max);
mSort(src, space, mid + 1, hight, max);
// 当剩下一个元素时,递归划分结束。开始进行merge归并操作
merge(space, des, low, mid, hight);
}
}
/**
* 两两合并
* @param src
* @param des
* @param low
* @param mid
* @param hight
*/
public static void merge(int[] src, int[] des, int low, int mid, int hight) {
int i = low;
int j = mid + 1;
int k = low;
// 将小的放到目的地中
while (i <= mid && j <= hight) {
if (src[i] < src[j]) {
des[k++] = src[i++];
} else {
des[k++] = src[j++];
}
}
// 若还剩几个尾部元素
while (i <= mid) {
des[k++] = src[i++];
}
// 若还剩几个尾部元素
while (j <= hight) {
des[k++] = src[j++];
}
}
}
二分查找又称折半查找,效率较高,但有限制条件。只能对线性表的顺序存储结构并且元素有序进行查找。可采用递归、非递归方式。
package datastructure.part.sort;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月28日 下午3:18:58
* @remark 二分查找法
*/
public class BinarySearch {
public static void main(String[] args) {
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8 };
int index = binarySearch(array, 1, 0, array.length - 1);
System.out.println("index:" + index);
index = binarySearch(array, 1);
System.out.println("index:" + index);
}
/**
* 非递归方式
* @param array
* @param keyword
* @return
*/
private static int binarySearch(int[] array, int keyword) {
int low = 0;
int hight = array.length - 1;
int mid;
while (low <= hight) {
mid = (low + hight) / 2;
if (keyword == array[mid]) {
return mid;
} else if (keyword < array[mid]) {
hight = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
/**
* 递归方式
* @param array
* @param keyword
* @param low
* @param hight
* @return
*/
private static int binarySearch(int[] array, int keyword, int low, int hight) {
if (low <= hight) {
int mid = (low + hight) / 2;
if (keyword == array[mid]) {
return mid;
} else if (keyword > array[mid]) {
return binarySearch(array, keyword, mid + 1, hight);
} else {
return binarySearch(array, keyword, low, mid - 1);
}
}
return -1;
}
}
**又称二叉查找树,其特点是如果右子树非空,右子树上所有结点的值均大于根节点的值。如果左子树非空,左子树上所有结点的值均小于根结点的值。左右子树又各是一棵二叉排序树。中序遍历即可获得一个有序序列。**Java中的TreeSet,其实就是一个二叉排序树,根据左小右大来放置元素。
package datastructure.part.one;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月8日 上午9:37:53
* @remark 数组结构的顺序栈
*/
public class Stack<T> {
private int maxSize; // 栈空间的大小
private T[] stackArray;// 顺序栈,采用泛型确定栈空间的存放的数据类型
private int top; // 栈顶指针
public Stack(int size) {
maxSize = size;
stackArray = (T[]) new Object[maxSize];
top = -1;
}
/**
* 入栈
* @param element
*/
public void push(T element) {
stackArray[++top] = element;
if (top > (maxSize - 1)){
top = maxSize - 1;
}
}
public void pretendedPush() {
++top;
if (top > (maxSize - 1)) {
top = maxSize - 1;
}
}
/**
* 出栈
* @return
*/
public T pop() {
if (top == -1) {
return null;
} else {
return stackArray[top--];
}
}
/**
* 查看栈顶元素,但是不出栈
* @return
*/
public T peek() {
return stackArray[top];
}
/**
* 判断栈是否空
* @return
*/
public boolean isEmpty() {
return top == -1;
}
/**
* 判断栈是否满
* @return
*/
public boolean isFull() {
return top == maxSize - 1;
}
}
借助栈空间,根据基数对原数取余数,把取到余数入栈,再对原数取整,循环该过程,直到取整的数为0时停止。最后再栈内元素全部出栈。
package datastructure.part.one;
/**
* @description
* @author Denghs
* @version 1.0,2018年4月7日 上午9:17:33
* @remark 进制转换
*/
public class BinaryConversion {
public static void main(String[] args) {
conversion(6, 2);
}
/**
* 进制转换的思想是,对一个原数不停的取余数
* @param number:原始数值
* @param decimalBinary:转换的进制,基数
*/
public static void conversion(int number, int decimalBinary) {
// 初始化栈空间,栈的大小。取数值整除基数再加1
Stack stack = new Stack(number / decimalBinary + 1);
while (number != 0) {
// 把余数放入栈中
stack.push(number % decimalBinary);
// 取整
number = number / decimalBinary;
}
while (!stack.isEmpty()) {
System.out.print(stack.pop());
}
}
}
回文的特点是:倒着写跟原来是一样的。代码的实现,用了三种方式。
第一种是判断非负整数是否是个回文数。采用的方式,是把原来的数值给回转过来,再跟原数值进行一次比较,相等则是回文数。
第二种是采用栈,对数值的长度进行劈叉,把前一半的数值一次入栈。然后再进行出栈,并与后一半数值进行比较,都相等则是回文数。该方式有一个BUG,当长度是奇数时,前一半数据的长度跟后一半数据的长度并不相等,会导致12321这种类型的数据,判断出来不是回文数。
第三种是获取数据的字节数组,采用头指针跟尾指针,每循环一次,拿头指针上的数据与尾指针上的数据进行比较,相等则头指针往前挪一位,尾指针往后挪一位。直到头指针小于等与尾指针,循环结束。
package datastructure.part.one;
/**
* @description
* @author Denghs
* @version 1.0,2018年3月5日 下午4:52:12
* @remark 判断回文数
*/
public class Symmetry {
public static void main(String[] args) {
System.out.println(symmetry(1221));
System.out.println(symmetry("哈哈哈哈"));
System.out.println(symmetry("456654"));
System.out.println(symmetryStr("45654"));
}
/**
* 判断一个数值是否是回文数。回文的特点是:倒着写跟原来是一样的。
*
* @param number
*/
public static boolean symmetry(int number) {
int i = 0;
int sum = 0;
int record = number;// 保存原记录数
while (true) {
i = number % 10;
sum = sum * 10 + i;
number /= 10;
if (number == 0)
break;
}
// 判断倒过来写的数,是否跟原来的数是一样的。
if (sum == record) {
return true;
} else {
return false;
}
}
/**
* 判断一个串是否是回文串。回文的特点是:倒着写跟原来是一样的。
* 使用栈的方式,进行比较。如果串的长度是一个奇数,会存在一个中间数没有对应的数进行比较。
* 则会返回false
* @param string
*/
public static boolean symmetry(String string) {
// 获取串字符的长度
char[] charArray = string.toCharArray();
// 初始化栈
Stack stack = new Stack(charArray.length / 2 + 1);
for (int i = 0; i < charArray.length / 2; i++) {
// 前一半的字符放入栈中
stack.push(charArray[i]);
}
for (int j = charArray.length / 2; j < charArray.length; j++) {
Character character = stack.pop();
// 栈内已经没有元素了或者有字符不相同
if (null == character || character != charArray[j]) {
return false;
}
}
return true;
}
/**
* 判断一个串是否是回文串。回文的特点是:倒着写跟原来是一样的。
* @param string
*/
public static boolean symmetryStr(String string) {
char[] charArray = string.toCharArray();
int start = 0;
int end = charArray.length - 1;
while (start <= end) {
if (charArray[start] != charArray[end]) {
return false;
}
start++;
end--;
}
return true;
}
}
package datastructure.part.one;
import java.util.Scanner;
/**
* @description
* @author Denghs
* @version 1.0,2018年2月7日 上午9:37:53
* @remark 汉诺塔问题
*/
public class HanNuoTa {
public static void main(String[] args) {
System.out.println("输入一个数字:");
Scanner scanner = new Scanner(System.in);
//操作数
int number = scanner.nextInt();
//三根柱子
String poleA = "柱子A";
String poleB = "柱子B";
String poleC = "柱子C";
//A柱子上的盘子,借助B柱子,移动到C柱子
hanNuoTa(number, poleA, poleB, poleC);
scanner.close();
}
/**
* @param number 盘子数
* @param poleA 盘子所在的柱子
* @param poleB 需要借力的柱子
* @param poleC 最终移动到的柱子
*/
private static void hanNuoTa(int number, String poleA, String poleB, String poleC) {
/*
如果是1个盘子
直接将A柱子上的盘子从A移动到C
否则
先将A柱子上的n-1个盘子,借助C移动B
直接将A柱子上的盘子从A移动到C
最后将B柱子上的n-1个盘子,借助A移动C*/
if(1 == number){
System.out.println("将编号为-" + number + "的盘子,直接从柱子-" + poleA + "移动到柱子-" + poleC);
}else{
hanNuoTa(number-1, poleA, poleC, poleB);
System.out.println("将编号为-" + number + "的盘子,直接从柱子-" + poleA + "移动到柱子-" + poleC);
hanNuoTa(number-1, poleB, poleA, poleC);
}
}
}