一、稳定性
直接插入排序、冒泡排序、归并排序、基数排序是稳定的
希尔排序、快速排序、选择排序、堆排序是不稳定的
二、时间复杂度
快速排序=堆排序=二路归并排序O(nlog2n)<希尔排序(n(log2n)^2)<直接插入=二分法插入=二路插入=共享栈插入=冒泡排序=直接选择排序(O(N^2))
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 | 复杂性 |
---|---|---|---|---|---|---|
直接插入排序 | O(n2)O(n2) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 稳定 | 简单 |
希尔排序 | O(nlog2n)O(nlog2n) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 不稳定 | 较复杂 |
直接选择排序 | O(n2)O(n2) | O(n2)O(n2) | O(n2)O(n2) | O(1)O(1) | 不稳定 | 简单 |
堆排序 | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(1)O(1) | 不稳定 | 较复杂 |
冒泡排序 | O(n2)O(n2) | O(n2)O(n2) | O(n)O(n) | O(1)O(1) | 稳定 | 简单 |
快速排序 | O(nlog2n)O(nlog2n) | O(n2)O(n2) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | 不稳定 | 较复杂 |
归并排序 | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(nlog2n)O(nlog2n) | O(n)O(n) | 稳定 | 较复杂 |
基数排序 | O(d(n+r))O(d(n+r)) | O(d(n+r))O(d(n+r)) | O(d(n+r))O(d(n+r)) | O(n+r)O(n+r) | 稳定 | 较复杂 |
三、交换排序(冒泡排序、快速排序)
1.冒泡排序
- 基本思想:两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
import java.util.Scanner;
public class bubbleSort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
String str = sc.nextLine();
String[] str1 = str.split(" ");
int[] num = new int[str1.length];
for(int i = 0; i < str1.length; i++) {
num[i] = Integer.parseInt(str1[i]);
}
System.out.println(bubblesort(num).toString());
}
}
public static StringBuffer bubblesort(int[] num) {
if(num.length < 1) {
return null;
}
int temp = 0;
for(int i = 0; i < num.length-1; i++) {
boolean flag = false;
for(int j = num.length-1; j > i; j--) {
if(num[j-1] > num[j]) {
temp = num[j-1];
num[j-1] = num[j];
num[j] = temp;
flag = true;
}
}
if(flag == false) {
break;
}
}
StringBuffer sb = new StringBuffer();
for(int i = 0; i < num.length; i++) {
sb.append(num[i]);
}
return sb;
}
}
2.快速排序
- 基本思想:在 当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X)一般选第一个或最后一个,用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和 R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即 R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过 程,直至所有无序子区中的数据元素均已排序为止。
public class QuickSort {
public static void quickSort(int[] arr,int low,int high){
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减。判断temp是否比右边的数小,是右边的数就不用移动
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增。判断左边的数是不是小于temp,是就不用移动
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
public static void main(String[] args){
int[] arr = {10,7,2,4,7,62,3,4,2,1,8,9,19};
quickSort(arr, 0, arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
四、选择排序(简单选择排序、堆排序、树形选择排序)
1.简单选择排序
- 基本思想:首先找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素,那么它就和自己交换。如果只剩下最后一个元素,就没必要排了,它就是最大的)。再次在剩下的元素中找最小的元素,将它与数组中的第二个元素交换。如此往复,直到将整个数组排序。
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int[] a = {2,3,1,4,6,5,7};
selectSort(a);
System.out.print(Arrays.toString(a));
}
public static void selectSort(int[] a) {
if(a == null || a.length < 1) {
return;
}
for(int i = 0; i < a.length; i++) {
int temp = a[i];
int flag = i;//将当前下标定义为最小下标值
for(int j = i + 1; j < a.length; j++) {
if(a[j] < temp) {
temp = a[j];
flag = j;
}
}
if(flag != i) {
a[flag] = a[i];
a[i] = temp;
}
}
}
}
2.堆排序
- 基本思想:堆排序正是利用小根堆(降序)(或大根堆(升序))来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无 序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐 步向前扩大到整个记录区。
public class HeadSorting {
public static void main(String []args){
int []arr = {8,9,5,7,6,2,1,3,4};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int []arr){
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
}
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i
* @param length
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
/**
* 交换元素
* @param arr
* @param a
* @param b
*/
public static void swap(int []arr,int a ,int b){
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
3.树形选择排序
- 基本思路:树形选择排序也叫锦标赛排序,我们可以类比比赛过程。有n个待排序的元素,把它们两两一组进行比较,取出较小的,然后在这n/2个较小者中再两两一组进行比较,取出较小的,重复上述步骤,直到取出最小元素。
这个过程用一棵满二叉树表示,在选出最小元素后,将这个元素对应的叶子节点的值置为∞,然后把不为∞的兄弟节点移到父节点的位置。一直重复这个过程就可以了
public class TreeSelectSort {
/* 对a[]按升序排列 */
public static void treeSelectSort(int[] a) {
int len = a.length;// 数组长度
int nodeSize = len * 2 - 1; // 对一个满二叉树,节点总数 = 叶子节点数*2-1
int[] tree = new int[nodeSize + 1]; // 这里将用数组表示二叉树的存储结构
/* 填充叶子节点 */
for (int i = len - 1, j = 0; i >= 0; i--, j++) {
tree[nodeSize - j] = a[i];
}
/* 填充其他节点 */
for (int i = nodeSize - len; i > 0; i--) {
tree[i] = tree[i * 2] < tree[i * 2 + 1] ? tree[i * 2] : tree[i * 2 + 1];
}
/* 将每次找出的最小元素移走 */
int index = 0;// 数组a的索引
int minIndex = 0;// 最小值的索引
while (index < len) {
int min = tree[1]; // 这是tree的根节点,也是最小元素
a[index++] = tree[1]; // 将tree中最小的元素取到a[0]中
minIndex = nodeSize;
/* 从最后的叶子节点开始,直到找到最小值的索引 */
while (tree[minIndex] != min) {
minIndex--;
}
tree[minIndex] = Integer.MAX_VALUE; // 将这个最小元素置为最大
/* 如果这个节点还有父节点,那么就将它的兄弟节点升到父亲节点位置 */
while (minIndex > 1) {// 根结点的索引是1
if (minIndex % 2 == 0) {// 这个节点是左节点
tree[minIndex / 2] = tree[minIndex] < tree[minIndex + 1] ? tree[minIndex] : tree[minIndex + 1];
minIndex = minIndex / 2;
} else {// 这个节点是右节点
tree[minIndex / 2] = tree[minIndex] < tree[minIndex - 1] ? tree[minIndex] : tree[minIndex - 1];
minIndex = minIndex / 2;
}
}
}
}/* treeSelectSort */
public static void main(String[] args) {
int[] a = { 12, 34, 23, 38, 65, 97, 76, 13 };
TreeSelectSort.treeSelectSort(a);
for (int i : a) {
System.out.print(i + " ");
}
}
}
五、插入排序(直接插入排序、希尔排序)
1.直接插入排序
- 基本思路:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] a = {2,4,3,1,6,5,8};
sort(a);
System.out.print(Arrays.toString(a));
}
public static void sort(int[] a) {
for(int i = 1; i < a.length; i++) {
int temp = a[i]; //待比较的数
int j = i;
while(j > 0 && temp < a[j-1]) {
a[j] = a[j-1];
j--;
}
if(i != j) {
a[j] = temp;
}
}
}
}
2.希尔排序
- 基本思路:先 取一个小于n的整数d1(d/2)作为第一个增量,把文件的全部记录分成d1组。所有距离为d1的倍数的记录放在同一组中。先在各组内进行直接插入排序,然后取第二 个增量d2<d1重复上述的分组和排序,直到所取的增量dt=1,即所有记录放在同一组中进行直接插入排序为止。该方法实际上是一种分组插入方法。
public class ShellSort {
public static void main(String[] args) {
int[] arr = {2,3,1,4,6,8,5,7};
System.out.println("希尔排序前:"+Arrays.toString(arr));
shellSort(arr);
System.out.println("希尔排序后:"+Arrays.toString(arr));
}
/**
*
* *希尔排序
* @param arr 待排数组
*/
public static void shellSort(int[] arr) {
for(int gap=arr.length/2; gap>0; gap/=2) { /*步长逐渐减小*/
for(int i=gap; i<arr.length; i++) { /*在同一步长内*/
//同一步长内排序方式是插入排序
int temp = arr[i], j; //待排元素
//j-gap代表有序数组中最大数的下标,j-pag表示有序数组的前一个元素,减pag是减去偏移量就是步长
for(j=i; j>=gap && temp<arr[j-gap]; j-=gap)
arr[j] = arr[j-gap]; //原有序数组最大的后移一位
arr[j] = temp; //找到了合适的位置插入
}
}
}
}
六、归并排序
- 基本思路:
1. 从下往上的归并排序:将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有 序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就 得到了我们想要的排序结果。
2. 从上往下的归并排序:它与"从下往上"在排序上是反方向的。它基本包括3步:
① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]
/*
* 将一个数组中的两个相邻有序区间合并成一个
*
* 参数说明:
* a -- 包含两个有序区间的数组
* start -- 第1个有序区间的起始地址。
* mid -- 第1个有序区间的结束地址。也是第2个有序区间的起始地址。
* end -- 第2个有序区间的结束地址。
*/
void merge(int a[], int start, int mid, int end)
{
int *tmp = (int *)malloc((end-start+1)*sizeof(int)); // tmp是汇总2个有序区的临时区域
int i = start; // 第1个有序区的索引
int j = mid + 1; // 第2个有序区的索引
int k = 0; // 临时区域的索引
while(i <= mid && j <= end)
{
if (a[i] <= a[j])
tmp[k++] = a[i++];
else
tmp[k++] = a[j++];
}
while(i <= mid)
tmp[k++] = a[i++];
while(j <= end)
tmp[k++] = a[j++];
// 将排序后的元素,全部都整合到数组a中。
for (i = 0; i < k; i++)
a[start + i] = tmp[i];
free(tmp);
}
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] a = {3,4,2,1,6,5};
int high = a.length-1;
sort(a, 0, high);
System.out.print(Arrays.toString(a));
}
public static int[] sort(int[] a,int low,int high){
int mid = (low+high)/2;
if(low<high){
sort(a,low,mid);
sort(a,mid+1,high);
//左右归并
merge(a,low,mid,high);
}
return a;
}
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i= low;
int j = mid+1;
int k=0;
// 把较小的数先移到新数组中
while(i<=mid && j<=high){
if(a[i]<a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i<=mid){
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while(j<=high){
temp[k++] = a[j++];
}
// 把新数组中的数覆盖nums数组
for(int x=0;x<temp.length;x++){
a[x+low] = temp[x];
}
}
}
七、基数排序
基本思路:将整数按位数切割成不同的数字,然后按每个位数分别比较。将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
public class BaseSort {
/*
* 获取数组a中最大值
*
* 参数说明:
* a -- 数组
* n -- 数组长度
*/
private static int getMax(int[] a) {
int max;
max = a[0];
for (int i = 1; i < a.length; i++)
if (a[i] > max)
max = a[i];
return max;
}
/*
* 对数组按照"某个位数"进行排序(桶排序)
*
* 参数说明:
* a -- 数组
* exp -- 指数。对数组a按照该指数进行排序。
*
* 例如,对于数组a={50, 3, 542, 745, 2014, 154, 63, 616};
* (01) 当exp=1表示按照"个位"对数组a进行排序
* (02) 当exp=10表示按照"十位"对数组a进行排序
* (03) 当exp=100表示按照"百位"对数组a进行排序
* ...
*/
private static void countSort(int[] a, int exp) {
//int output[a.length]; // 存储"被排序数据"的临时数组
int[] output = new int[a.length]; // 存储"被排序数据"的临时数组
int[] buckets = new int[10];
// 将数据出现的次数存储在buckets[]中
for (int i = 0; i < a.length; i++)
buckets[ (a[i]/exp)%10 ]++;
// 更改buckets[i]。目的是让更改后的buckets[i]的值,是该数据在output[]中的位置。
for (int i = 1; i < 10; i++)
buckets[i] += buckets[i - 1];
// 将数据存储到临时数组output[]中
for (int i = a.length - 1; i >= 0; i--) {
output[buckets[ (a[i]/exp)%10 ] - 1] = a[i];
buckets[ (a[i]/exp)%10 ]--;
}
// 将排序好的数据赋值给a[]
for (int i = 0; i < a.length; i++)
a[i] = output[i];
output = null;
buckets = null;
}
/*
* 基数排序
*
* 参数说明:
* a -- 数组
*/
public static void radixSort(int[] a) {
int exp; // 指数。当对数组按各位进行排序时,exp=1;按十位进行排序时,exp=10;...
int max = getMax(a); // 数组a中的最大值
// 从个位开始,对数组a按"指数"进行排序
for (exp = 1; max/exp > 0; exp *= 10)
countSort(a, exp);
}
public static void main(String[] args) {
int i;
int a[] = {53, 3, 542, 748, 14, 214, 154, 63, 616};
System.out.printf("before sort:");
for (i=0; i<a.length; i++)
System.out.printf("%d ", a[i]);
System.out.printf("\n");
radixSort(a); // 基数排序
System.out.printf("after sort:");
for (i=0; i<a.length; i++)
System.out.printf("%d ", a[i]);
System.out.printf("\n");
}
}
八、二分查找排序
- 基本思路:
将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;
如果x>a[n/2],则继续在数组a的左半边查找x,如果x<a[n/2],则在数组a的右半边查找x.
- 要求:(1)必须采用顺序存储结构 (2).必须按关键字大小有序排列
public class dichotomy {
public static void main(String[] args) {
int[] a = {1,2,3,4,5,6,7,8};
int num = 2;
System.out.println(recursiveBinarySearch(a,num,0,a.length-1));
System.out.println(loopBinarySearch(a,num,0,a.length-1));
}
//递归方法
public static int recursiveBinarySearch(int[] a, int num, int begin, int end) {
if(num < a[0] || num > a[a.length-1] || begin > end) {
return -1;
}
int mid = (begin + end)/2;
if(num == a[mid]) {
return mid;
}else if(num < a[mid]) {
return recursiveBinarySearch(a,num,0,mid-1);
}else {
return recursiveBinarySearch(a,num,mid+1,end);
}
}
//循环二分法
public static int loopBinarySearch(int[] a, int num, int begin, int end) {
int beginIndex = begin;
int endIndex = end;
while(beginIndex < endIndex && (num < a[endIndex] && num > a[beginIndex])) {
int mid = (beginIndex+endIndex)/2;
if(num == a[mid]) {
return mid;
}else if(num > a[mid]) {
beginIndex = mid + 1;
}else {
endIndex = mid -1;
}
}
return -1;
}
}
九、二叉树的遍历
- 基本思路:前序遍历:根左右,中序遍历:左根右,后序遍历:左右根
1.先创建结点类
public class Node {
private int data;
private Node leftNode;
private Node rightNode;
public Node(int data, Node leftNode, Node rightNode){
this.data = data;
this.leftNode = leftNode;
this.rightNode = rightNode;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getLeftNode() {
return leftNode;
}
public void setLeftNode(Node leftNode) {
this.leftNode = leftNode;
}
public Node getRightNode() {
return rightNode;
}
public void setRightNode(Node rightNode) {
this.rightNode = rightNode;
}
}
2.利用递归方法遍历二叉树
public class BinaryTree {
public Node init() {//注意必须逆序建立,先建立子节点,再逆序往上建立,因为非叶子结点会使用到下面的节点,而初始化是按顺序初始化的,不逆序建立会报错
Node J = new Node(8, null, null);
Node H = new Node(4, null, null);
Node G = new Node(2, null, null);
Node F = new Node(7, null, J);
Node E = new Node(5, H, null);
Node D = new Node(1, null, G);
Node C = new Node(9, F, null);
Node B = new Node(3, D, E);
Node A = new Node(6, B, C);
return A; //返回根节点
}
public void printNode(Node node){
System.out.print(node.getData());
}
public void theFirstTraversal(Node root) { //先序遍历
printNode(root);
if (root.getLeftNode() != null) { //使用递归进行遍历左孩子
theFirstTraversal(root.getLeftNode());
}
if (root.getRightNode() != null) { //递归遍历右孩子
theFirstTraversal(root.getRightNode());
}
}
public void theInOrderTraversal(Node root) { //中序遍历
if (root.getLeftNode() != null) {
theInOrderTraversal(root.getLeftNode());
}
printNode(root);
if (root.getRightNode() != null) {
theInOrderTraversal(root.getRightNode());
}
}
public void thePostOrderTraversal(Node root) { //后序遍历
if (root.getLeftNode() != null) {
thePostOrderTraversal(root.getLeftNode());
}
if(root.getRightNode() != null) {
thePostOrderTraversal(root.getRightNode());
}
printNode(root);
}
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
Node root = tree.init();
System.out.println("先序遍历");
tree.theFirstTraversal(root);
System.out.println("");
System.out.println("中序遍历");
tree.theInOrderTraversal(root);
System.out.println("");
System.out.println("后序遍历");
tree.thePostOrderTraversal(root);
System.out.println("");
}
}
2.利用堆栈方式实现
import java.util.Stack;
public class BinaryTree1 {
public Node init() {//注意必须逆序建立,先建立子节点,再逆序往上建立,因为非叶子结点会使用到下面的节点,而初始化是按顺序初始化的,不逆序建立会报错
Node J = new Node(8, null, null);
Node H = new Node(4, null, null);
Node G = new Node(2, null, null);
Node F = new Node(7, null, J);
Node E = new Node(5, H, null);
Node D = new Node(1, null, G);
Node C = new Node(9, F, null);
Node B = new Node(3, D, E);
Node A = new Node(6, B, C);
return A; //返回根节点
}
public void printNode(Node node){
System.out.print(node.getData());
}
public void theFirstTraversal_Stack(Node root) { //先序遍历
Stack<Node> stack = new Stack<Node>();
Node node = root;
while (node != null || stack.size() > 0) { //将所有左孩子压栈
if (node != null) { //压栈之前先访问
printNode(node);
stack.push(node);
node = node.getLeftNode();
} else {
node = stack.pop();
node = node.getRightNode();
}
}
}
public void theInOrderTraversal_Stack(Node root) { //中序遍历
Stack<Node> stack = new Stack<Node>();
Node node = root;
while (node != null || stack.size() > 0) {
if (node != null) {
stack.push(node); //直接压栈
node = node.getLeftNode();
} else {
node = stack.pop(); //出栈并访问
printNode(node);
node = node.getRightNode();
}
}
}
public void thePostOrderTraversal_Stack(Node root) { //后序遍历
Stack<Node> stack = new Stack<Node>();
Stack<Node> output = new Stack<Node>();//构造一个中间栈来存储逆后序遍历的结果
Node node = root;
while (node != null || stack.size() > 0) {
if (node != null) {
output.push(node);
stack.push(node);
node = node.getRightNode();
} else {
node = stack.pop();
node = node.getLeftNode();
}
}
//System.out.println(output.size());
while (output.size() > 0) {
printNode(output.pop());
}
}
public static void main(String[] args) {
BinaryTree1 tree = new BinaryTree1();
Node root = tree.init();
System.out.println("先序遍历");
tree.theFirstTraversal_Stack(root);
System.out.println("");
System.out.println("中序遍历");
tree.theInOrderTraversal_Stack(root);
System.out.println("");
System.out.println("后序遍历");
tree.thePostOrderTraversal_Stack(root);
System.out.println("");
}
}
十、深度优先遍历和广度优先遍历
1.深度优先遍历:根据深度优先遍历的特点我们利用Java集合类的栈Stack先进后出的特点来实现
- 首先节点 1 进栈,节点1在栈顶;
- 然后节点1出栈,访问节点1,节点1的孩子节点3进栈,节点2进栈;
- 节点2在栈顶,然后节点2出栈,访问节点2
- 节点2的孩子节点5进栈,节点4进栈
- 节点4在栈顶,节点4出栈,访问节点4,
- 节点4左右孩子为空,然后节点5在栈顶,节点5出栈,访问节点5;
- 节点5左右孩子为空,然后节点3在站顶,节点3出栈,访问节点3;
- 节点3的孩子节点7进栈,节点6进栈
- 节点6在栈顶,节点6出栈,访问节点6;
- 节点6的孩子为空,这个时候节点7在栈顶,节点7出栈,访问节点7
- 节点7的左右孩子为空,此时栈为空,遍历结束。
2.广度优先遍历:根据广度优先遍历的特点我们利用Java数据结构队列Queue来实现
- 节点1进队,节点1出队,访问节点1
- 节点1的孩子节点2进队,节点3进队。
- 节点2出队,访问节点2,节点2的孩子节点4进队,节点5进队;
- 节点3出队,访问节点3,节点3的孩子节点6进队,节点7进队;
- 节点4出队,访问节点4,节点4没有孩子节点。
- 节点5出队,访问节点5,节点5没有孩子节点。
- 节点6出队,访问节点6,节点6没有孩子节点。
- 节点7出队,访问节点7,节点7没有孩子节点,结束。
3.构造二叉树结构
public class TreeNode {
int data; TreeNode leftNode;
TreeNode rightNode;
public TreeNode() {
}
public TreeNode(int d) {
data=d;
}
public TreeNode(TreeNode left,TreeNode right,int d) {
leftNode=left;
rightNode=right;
data=d;
}
}
4.代码实现
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class DeepFirstSort {
public static void main(String[] args) {
TreeNode head=new TreeNode(1);
TreeNode second=new TreeNode(2);
TreeNode three=new TreeNode(3);
TreeNode four=new TreeNode(4);
TreeNode five=new TreeNode(5);
TreeNode six=new TreeNode(6);
TreeNode seven=new TreeNode(7);
head.rightNode=three;
head.leftNode=second;
second.rightNode=five;
second.leftNode=four;
three.rightNode=seven;
three.leftNode=six;
System.out.print("广度优先遍历结果:");
new DeepFirstSort().BroadFirstSearch(head);
System.out.println();
System.out.print("深度优先遍历结果:");
new DeepFirstSort().depthFirstSearch(head); }
//广度优先遍历是使用队列实现的
public void BroadFirstSearch(TreeNode nodeHead) {
if(nodeHead==null) {
return;
}
Queue<TreeNode> myQueue=new LinkedList<>();
myQueue.add(nodeHead);
while(!myQueue.isEmpty()) {
TreeNode node=myQueue.poll();
System.out.print(node.data+" ");
if(null!=node.leftNode) {
myQueue.add(node.leftNode);
//广度优先遍历,我们在这里采用每一行从左到右遍历
}
if(null!=node.rightNode) {
myQueue.add(node.rightNode);
}
}
}
//深度优先遍历
public void depthFirstSearch(TreeNode nodeHead) {
if(nodeHead==null) {
return;
}
Stack<TreeNode> myStack=new Stack<>();
myStack.add(nodeHead);
while(!myStack.isEmpty()) {
TreeNode node=myStack.pop();
//弹出栈顶元素
System.out.print(node.data+" ");
if(node.rightNode!=null) {
myStack.push(node.rightNode);
//深度优先遍历,先遍历左边,后遍历右边,栈先进后出
}
if(node.leftNode!=null) {
myStack.push(node.leftNode);
}
}
}
}
5.队列
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
6.广度优先就相当于,先将头节点入队列,然后头节点出队列保存在node中,再将头节点对应的左右孩子依次入队列,在第二次循环中先将左孩子出队列,右孩子不动。再将左孩子的左右孩子入队列。第三次循环,将右孩子出队列,右孩子的左右孩子依次出队列。依次循环,一次只出一个元素,并将出队列相对应的左右孩子入队列。
7.栈
1-->public Stack()创建一个空堆栈
2-->public boolean empty()测试堆栈是否为空;
3-->public E pop()移除堆栈顶部的对象,并作为此函数的值返回该对象。
4-->public E push(E item)把项压入堆栈顶部
5-->public E peek()查看堆栈顶部的对象,但不从堆栈中移除它。
6-->public boolean empty()测试堆栈是否为空
8.深度优先遍历相当于先将根节点入栈再出栈,然后将根节点对应的右孩子、左孩子依次入栈。第二次循环,将栈顶的左孩子出栈,再将左孩子对应的右孩子和左孩子依次入栈。第三次循环将栈顶的左孩子出栈,该左孩子的左右子树为空,则不进行入栈操作。第四次循环将栈顶的右孩子出栈。依次循环。
十一、利用栈实现字符串反转
import java.util.Stack;
public class StringReverse {
public static void main(String[] args) {
String[] a = {"a","s","f","d","g","h","j","k"};
Stack<String> st = new Stack<>();
for(int i = 0; i < a.length; i++) {
st.push(a[i]);
}
while(!st.isEmpty()){
System.out.print(st.pop());
}
}
}
十二、N进制转为M进制
先将N进制转为10进制(按位乘进制数),再将10进制转为M进制(求余再反转)
public class NToM {
/*public static void main(String[] args) {
String num = "1010";
int N = 2;
int M = 16;
int a = Integer.parseInt(num,N);//十进制
String b = Integer.toHexString(a);//十六进制
String c = Integer.toOctalString(a);//八进制
System.out.println(b);
System.out.println(c);
}*/
public static void main(String[] args) {
String origin ="101010";
int N = 2;
int M = 16;
long ten = NToTen(origin, N);
System.out.println(TenToAny(ten,M));
}
//N进制转10进制
private static int NToTen(String origin, int N) {
String[] elements = origin.split("");
int mm = 0;
for(int i = 0; i < elements.length; i++) {
Character c = elements[i].charAt(0);
mm += getByteNum(c)*Math.pow(N, elements.length-1-i);
}
System.out.println(mm);
return mm;
}
//将字符转化成数字
private static int getByteNum(Character c) {
return Character.getNumericValue(c);
}
//10进制转为任意进制
private static String TenToAny(long ten, int M) {
long yu = 0;
StringBuffer sb = new StringBuffer();
String code = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while(ten > 0) {
yu = ten % M;
ten = ten / M;
sb.append(code.charAt((int) yu));
}
sb.reverse();
return sb.toString();
}
}
十三、归并扩展
1.两个有序数组合并成一个
public class TwoArrayToOne {
public static void main(String[] args) {
int[] a = {1,3,5,7};
int[] b = {2,4,6,8};
int[] result = twoArrayToOne(a,b);
for(int i : result) {
System.out.print(i);
}
}
public static int[] twoArrayToOne(int[] a, int[] b) {
if(a == null && b == null) {
return null;
}
if(a == null) {
return b;
}
if(b == null) {
return a;
}
int la = a.length;
int lb = b.length;
int starta = 0;
int startb = 0;
int result = 0;
int[] rs = new int[la+lb];
while(starta < la && startb < lb) {
if(a[starta] < b[startb]) {
rs[result++] = a[starta++];
}else {
rs[result++] = b[startb++];
}
}
while(starta < la) {
rs[result++] = a[starta++];
}
while(startb < lb) {
rs[result++] = b[startb++];
}
return rs;
}
}
2.两个有序数组合并并去重
import java.util.TreeSet;
public class TwoArrayToOne1 {
public static void main(String[] args) {
int[] a = {1,3,5,7,9};
int[] b = {2,4,6,7,8,9};
int[] result = sort(a,b);
for(int i : result) {
System.out.print(i);
}
}
public static int[] sort(int[] a, int[] b) {
if(a == null && b == null) {
return null;
}
if(a == null) {
return b;
}
if(b == null) {
return a;
}
TreeSet<Integer> tree = new TreeSet<>();
int sa = 0;
int sb = 0;
while(sa < a.length) {
tree.add(a[sa++]);
}
while(sb < b.length) {
tree.add(b[sb++]);
}
Object[] obj = tree.toArray();
int[] result = new int[obj.length];
for(int i = 0; i<obj.length; i++) {
result[i] = (int) obj[i];
}
return result;
}
}
相关知识点:
1.利用TreeSet的唯一和有序,将数组全部存入tree set中