- 递归算法
- /**
- *
- * @author SunnyMoon
- */
- /**
- * 概念介绍:
- * 递归是一种方法(函数)调用自已编程技术。
- * 递归就是程序设计中的数学归纳法。
- * 例如:tri(n)=1 if n=1
- * tri(n)=n+tri(n-1) if n>1
- * 可能while循环方法执行的速度比递归方法快,但是为什么采用递归呢。
- * 采用递归,是因为它从概念上简化了问题,而不是因为它提高效率。
- */
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class Recursion {//求三角数字的递归算法:1,3,6,10,15,21, ......
- static int theNumber;
- public static void main(String[] args) throws IOException {
- System.out.print("Enter a number: ");
- theNumber = getInt();
- //使用递归时调用,默认
- int theAnswer = triangle(theNumber);
- //使用非递归时调用
- //int theAnswer=triangle2(theNumber);
- System.out.println("Result: " + theAnswer);
- }
- public static int triangle(int n) {//递归方法,循环调用
- if (n == 1) {
- return 1;
- } else {
- return (n + triangle(n - 1));
- }
- }
- public static int triangle2(int n) {//非递归方法
- int total = 0;
- while (n > 0) {
- total = total + n--;
- }
- return total;
- }
- public static String getString() throws IOException {
- InputStreamReader isr = new InputStreamReader(System.in);
- BufferedReader br = new BufferedReader(isr);
- String s = br.readLine();
- return s;
- }
- public static int getInt() throws IOException {
- String s = getString();
- return Integer.parseInt(s);
- }
- }
- /**
- * 运行结果:
- * Enter a number:
- * 3
- * Result: 6
- */
- /**
- * 总结:
- * 使用非递归的方式更简洁,更易懂,运行效率更高,为什么还要用递归的算法呢。
- * 递归使规模逐渐降低的方式解决问题,以这种统一的方式解决足够复杂的问题。
- */
- /**
- * 概念介绍:
- *
- * 树:树由边连接的节点构成。
- * 多路树:节点可以多于两个。
- * 路径:顺着连接点的边从一个节点到另一个节点,所以过的节点顺序排列就称做路径。
- * 根:树的顶端节点称为根。
- * 父节点:每个节点都有一条边向上连接到另一个节点,这个节点就称为父节点。
- * 子节点:每个节点都可能有一条或多条边向下连接其它节点,下面这些节点就称为子节点。
- * 叶节点:没有子节点的节点为叶子节点或叶节点。
- * 子树:每个节点都可以作为子树的根,它和它所有的子节点都包含在子树中。
- * 访问:当程序控制流程到达某个节点时,就称为“访问”这个节点。
- * 遍历:遍历树意味着要遵循某种特定的顺序访问树中所有的节点。
- * 层:一个节点的层数是指从根开始到这个节点有多少“代”。一般根为第0层。
- * 关键字:对象中通常会有一个数据域被指定为关键字,通常使用这个关键字进行查询等操作。
- * 二叉树:如果树中每个节点最多只能有两个子节点,这样的特殊的树就是二叉树。
- * 二叉搜索树:二叉树的一个节点的左子节点的关键字值小于这个节点,右子节点的关键字值大
- * 于或等于这个父节点。
- * 平衡树与非平衡树:左子节点与左子节点对称的树为平衡树,否则就是非平衡树。
- * 完全二叉树:二叉树的最后一层都是叶子结点,其它各层都有左右子树,也叫满二叉树。
- *
- * 为什么用二叉树:1.二叉树结合了另外两种数据结构的优点:一种是有序数组,另一种是链表。
- * 在树中查找数据的速度和在有序数组中查找的速度一样快,同时插入的速度
- * 和删除的速度和链表的速度一样。
- * 2.在有序数组中插入数据项太慢:用二分查找法可以在有序数据中快速的查找
- * 特定的值,查找所需时间复杂度为O(logN)。然而插入和删除是非常低效的。
- * 3.在链表中查找太慢:链表的插入和删除操作都很快,时间复杂度是O(1)。
- * 然而查找数据项是非常低效的。
- * 二叉树的效率:时间复杂度为O(logN)。树对所有的数据存储操作都很高效。
- *
- * 程序介绍:对树的一些常用操作进行了封装,包括查询,插入,删除,遍历二叉树(中序,后序,前序)
- * 以及以树的方式显示二对树的各个结点。
- *
- */
- /**
- *
- * @author SunnyMoon
- */
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- /**
- * 定义树的结点类
- */
- class Node {
- public int iData;//关键字
- public double dData;//数据项
- public Node leftChild;//左子树
- public Node rightChild;//右子树
- public void displayNode() {//输出结点内容
- System.out.print("【");
- System.out.print("关键字: "+iData);
- System.out.print(",");
- System.out.print("值:"+dData);
- System.out.print("】");
- }
- }
- /**
- * 定义二叉树类
- */
- class Tree {
- private Node root;
- public Tree() {
- root = null;
- }
- /**
- * 查找
- * @param key
- * @return
- */
- public Node find(int key) {
- Node current = root;
- while (current.iData != key) {
- if (key < current.iData) {
- current = current.leftChild;
- } else {
- current = current.rightChild;
- }
- if (current == null) {
- return null;
- }
- }
- return current;
- }
- /**
- * 插入
- * @param id
- * @param dd
- */
- public void insert(int id, double dd) {
- Node newNode = new Node();
- newNode.iData = id;
- newNode.dData = dd;
- if (root == null) {
- root = newNode;
- } else {
- Node current = root;
- Node parent;
- while (true) {
- parent = current;
- if (id < current.iData) {
- current = current.leftChild;
- if (current == null) {
- parent.leftChild = newNode;
- return;
- }
- } else {
- current = current.rightChild;
- if (current == null) {
- parent.rightChild = newNode;
- return;
- }
- }
- }
- }
- }
- /**
- * 删除
- * @param key
- * @return
- */
- public boolean delete(int key) {
- Node current = root;
- Node parent = root;
- boolean isLeftChild = true;
- while (current.iData != key) {
- parent = current;
- if (key < current.iData) {
- isLeftChild = true;
- current = current.leftChild;
- } else {
- isLeftChild = false;
- current = current.rightChild;
- }
- if (current == null) {
- return false;
- }
- }
- if (current.leftChild == null && current.rightChild == null) {
- if (current == root) {
- root = null;
- } else if (isLeftChild) {
- parent.leftChild = null;
- } else {
- parent.rightChild = null;
- }
- } else if (current.rightChild == null) {
- if (current == root) {
- root = current.leftChild;
- } else if (isLeftChild) {
- parent.leftChild = current.leftChild;
- } else {
- parent.rightChild = current.leftChild;
- }
- } else if (current.leftChild == null) {
- if (current == root) {
- root = current.rightChild;
- } else if (isLeftChild) {
- parent.leftChild = current.rightChild;
- } else {
- parent.rightChild = current.rightChild;
- }
- } else {
- Node successor = getSuccessor(current);
- if (current == root) {
- root = successor;
- } else if (isLeftChild) {
- parent.leftChild = successor;
- } else {
- parent.rightChild = successor;
- }
- successor.leftChild = current.leftChild;
- }
- return true;
- }
- /**
- * 遍历二叉树
- * @param traverseType
- */
- public void traverse(int traverseType) {
- switch (traverseType) {
- case 1:
- System.out.print("\n" + "前序遍历(Preorder traversal): ");
- preOrder(root);
- break;
- case 2:
- System.out.print("\n" + "中序遍历(Inorder traversal): ");
- inOrder(root);
- break;
- case 3:
- System.out.print("\n" + "后序遍历(Postorder traversal): ");
- postOrder(root);
- break;
- }
- System.out.println();
- }
- /**
- * 定义定位到后序结点方法
- * @param delNode
- * @return
- */
- private Node getSuccessor(Node delNode) {
- Node successorParent = delNode;
- Node successor = delNode;
- Node current = delNode.rightChild;
- while (current != null) {
- successorParent = successor;
- successor = current;
- current = current.leftChild;
- }
- if (successor != delNode.rightChild) {
- successorParent.leftChild = successor.rightChild;
- successor.rightChild = delNode.rightChild;
- }
- return successor;
- }
- /**
- * 前序遍历
- * @param localRoot
- */
- private void preOrder(Node localRoot) {
- if (localRoot != null) {
- System.out.print(localRoot.iData + " ");
- preOrder(localRoot.leftChild);
- preOrder(localRoot.rightChild);
- }
- }
- /**
- * 中序遍历
- * @param localRoot
- */
- private void inOrder(Node localRoot) {
- if (localRoot != null) {
- inOrder(localRoot.leftChild);
- System.out.print(localRoot.iData + " ");
- inOrder(localRoot.rightChild);
- }
- }
- /**
- * 后序遍历
- * @param localRoot
- */
- private void postOrder(Node localRoot) {
- if (localRoot != null) {
- postOrder(localRoot.leftChild);
- postOrder(localRoot.rightChild);
- System.out.print(localRoot.iData + " ");
- }
- }
- /**
- * 把关键字按树型输出
- * ‘--’表示树中这个位置的结点不存在。
- */
- public void displayTree() {
- Stack globalStack = new Stack(1000);
- globalStack.push(root);
- int nBlanks = 32;
- boolean isRowEmpty = false;
- System.out.println(
- "-----------------------------------------------------------------------");
- while (isRowEmpty == false) {
- Stack localStack = new Stack(1000);
- isRowEmpty = true;
- for (int j = 0; j < nBlanks; j++) {
- System.out.print(" ");
- }
- while (globalStack.isEmpty() == false) {
- Node temp = (Node) globalStack.pop();
- if (temp != null) {
- System.out.print(temp.iData);
- localStack.push(temp.leftChild);
- localStack.push(temp.rightChild);
- if (temp.leftChild != null || temp.rightChild != null) {
- isRowEmpty = false;
- }
- } else {
- System.out.print("..");
- localStack.push(null);
- localStack.push(null);
- }
- for (int j = 0; j < nBlanks * 2 - 2; j++) {
- System.out.print(" ");
- }
- }
- System.out.println();
- nBlanks /= 2;
- while (localStack.isEmpty() == false) {
- globalStack.push(localStack.pop());
- }
- }
- System.out.println(
- "-----------------------------------------------------------------------");
- }
- }
- /**
- * 使用的栈
- * @author Administrator
- */
- class Stack {
- private int maxSize;
- private Object[] stackArray;
- private int top;
- public Stack(int s) {
- maxSize = s;
- stackArray = new Object[maxSize];
- top = -1;
- }
- public void push(Object p) {
- stackArray[++top] = p;
- }
- public Object pop() {
- return stackArray[top--];
- }
- public Object peek() {
- return stackArray[top];
- }
- boolean isEmpty() {
- if (top == -1) {
- return true;
- } else {
- return false;
- }
- }
- }
- /**
- * 主方法
- * @author Administrator
- */
- class TreeAaa {
- public static void main(String[] args) throws IOException {
- int value;
- Tree theTree = new Tree();
- theTree.insert(12, 1.5);
- theTree.insert(15, 2.4);
- theTree.insert(22, 5.6);
- theTree.insert(33, 7.1);
- theTree.insert(55, 3.3);
- theTree.insert(26, 8.7);
- theTree.insert(17, 2.3);
- theTree.insert(8, 6.9);
- theTree.insert(6, 8.4);
- theTree.insert(14, 7.0);
- theTree.insert(23, 1.8);
- theTree.insert(38, 2.9);
- while (true) {
- System.out.print("输入想执行的操作的英文首字母:");
- System.out.print("插入(Insert), 查找(Find), 删除(Delete), 遍历(Traverse): ");
- int choice = getChar();
- switch (choice) {
- case 's':
- theTree.displayTree();
- break;
- case 'i':
- System.out.print("输入想要插入的值: ");
- value = getInt();
- theTree.insert(value, value + 0.9);
- break;
- case 'f':
- System.out.print(("输入想要查找的关键字: "));
- value = getInt();
- Node found = theTree.find(value);
- if (found != null) {
- System.out.print("成功查找: ");
- found.displayNode();
- System.out.print("\n");
- } else {
- System.out.print("不存在所查询关键字");
- }
- System.out.print("输入的关键字:" + value + "\n");
- break;
- case 'd':
- System.out.print("输入想要删除的关键字: ");
- value = getInt();
- boolean didDelete = theTree.delete(value);
- if (didDelete) {
- System.out.print("删除的值:" + value + "\n");
- } else {
- System.out.print("不能执行删除操作");
- }
- System.out.println(value);
- //System.out.print(value + "\n");
- break;
- case 't':
- System.out.print("输入遍历类型 1, 2 或 3:");
- value = getInt();
- theTree.traverse(value);
- break;
- default:
- System.out.println("非法输入");
- }
- }
- }
- public static String getString() throws IOException {
- InputStreamReader isr = new InputStreamReader(System.in);
- BufferedReader br = new BufferedReader(isr);
- String s = br.readLine();
- return s;
- }
- public static char getChar() throws IOException {
- String s = getString();
- return s.charAt(0);
- }
- public static int getInt() throws IOException {
- String s = getString();
- return Integer.parseInt(s);
- }
- /**
- * 运行结果:
- * 输入想执行的操作的英文首字母:插入(Insert), 查找(Find), 删除(Delete), 遍历(Traverse): s
- *-----------------------------------------------------------------------
- * 12
- * 8 15
- * 6 .. 14 22
- * .. .. .. .. .. .. 17 33
- * .. .. .. .. .. .. .. .. .. .. .. .. .. .. 26 55
- * ........................................................23..38..
- *-----------------------------------------------------------------------
- *输入想执行的操作的英文首字母:插入(Insert), 查找(Find), 删除(Delete), 遍历(Traverse): i
- *输入想要插入的值: 3
- *输入想执行的操作的英文首字母:插入(Insert), 查找(Find), 删除(Delete), 遍历(Traverse): f
- *输入想要查找的关键字: 14
- *成功查找: {14,7.0}
- *输入的关键字:14
- *输入想执行的操作的英文首字母:插入(Insert), 查找(Find), 删除(Delete), 遍历(Traverse):
- */
- /**
- * 总结:
- * 树结合了数组和链表的优点,是一种非常高效的数据结构。
- */
-
快速排序/**
- /**
- *
- * @author SunnyMoon
- */
- ////////////////////////////////////////////////////////////////////////////////
- /*******************************************************************************
- * 概念介绍:
- * *****************************************************************************
- *
- * 简单排序:
- * 包括冒泡排序,选择排序和插入排序,是一些容易实现的,但速度比较慢的排序算法。
- *
- * 高级排序:
- * 快速排序,希尔排序和快速排序比简单排序快很多。本节主要介绍快速排序。
- *
- * 归并排序:
- * 在递归算法中已经介绍过,它需要的容易是原始空间的两倍,是一个严重的缺点。
- *
- * 希尔排序:
- * 不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法,
- * 在此算法基础之上增加了一个新的特性,提高了效率。
- *
- * 快速排序:
- * 不需要大量辅助空间,并且是通用排序算法中最快的排序算法,是基于划分的思想。
- * 快速排序算法本质上是通过把一个数组递归的划分为两个子数组。
- * 递归的基本步骤:
- * 1. 把数组划分成以一个元素为枢纽的左右两个子数组。
- * 2. 调用自身的左边和右边以步骤1递归。
- * 快速排序法的核心就是递归调用划分算法,直到基值的情况,这时数组就为有序的。
- * 快速排序的复杂度为:
- * O(N*logN)
- *
- * 影响效率的最大障碍:
- * 对枢纽数据的选择是影响排序的效率。例如本例子选择枢纽数据为数组的最后一个元素,
- * 这么选择只是为方便,然而却造成了特殊情况时效率极度下降,降到O(n2)。这种特情况就是当数据为逆序的时候。
- * 如果改变特殊情况给快速排序带来的致命影响呢,这将在下一专题中详细介绍。
- */
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * 定义一个数组类,封装了对自身数据的排序。
- */
- class ArrayIns {
- private long[] theArray;//定义数组
- private int nElems;//数组中的元素个数
- /**
- * 初始化
- * @param max
- */
- public ArrayIns(int max) {
- theArray = new long[max];
- nElems = 0;
- }
- /**
- * 为数组赋值
- * @param value
- */
- public void insert(long value) {
- theArray[nElems] = value;
- nElems++;
- }
- /**
- * 显示数组元素
- */
- public void display() {
- System.out.print("A=");
- for (int j = 0; j < nElems; j++) {
- System.out.print(theArray[j] + " ");
- }
- System.out.println("");
- }
- /**
- * 快速排序主方法
- */
- public void quickSort(){
- recQuickSort(0, nElems-1);
- }
- /**
- * 快速排序需递归调用的方法
- * @param left
- * @param right
- */
- public void recQuickSort(int left, int right) {
- if (right - left <= 0) {
- return;
- } else {
- long pivot = theArray[right];
- int partition = partitionIt(left, right, pivot);
- recQuickSort(left, partition - 1);
- recQuickSort(partition + 1, right);
- }
- }
- /**
- * 快速排序划分的核心方法
- * @param left
- * @param right
- * @param pivot
- * @return
- */
- public int partitionIt(int left, int right, long pivot) {
- int leftPtr = left-1;
- int rightPtr = right;
- while (true) {
- while (theArray[++leftPtr] < pivot)
- ;
- while (rightPtr > 0 && theArray[--rightPtr] > pivot)
- ;
- if (leftPtr >= rightPtr) {
- break;
- } else {
- swap(leftPtr, rightPtr);
- }
- }
- swap(leftPtr,right);
- return leftPtr;
- }
- /**
- * 交换数据中两个位置的数据
- * @param dex1
- * @param dex2
- */
- public void swap(int dex1, int dex2) {
- long temp = theArray[dex1];
- theArray[dex1] = theArray[dex2];
- theArray[dex2] = temp;
- }
- }
- /**
- * 执行算法的主类
- */
- public class QuickSort1 {
- public static void main(String[] args) {
- int maxSize = 16;
- ArrayIns arr = new ArrayIns(maxSize);
- for (int j = 0; j < maxSize; j++) {
- long n = (int) (java.lang.Math.random()*99);
- arr.insert(n);
- }
- System.out.println("显示排序前数据");
- arr.display();
- arr.quickSort();
- System.out.println("显示排序后数据");
- arr.display();
- }
- }
- /**
- *
- * 显示排序前数据:
- * A=9 14 33 27 66 89 53 32 72 14 46 33 13 79 28 26
- * 显示排序后数据:
- * A=9 13 14 14 26 27 28 32 33 33 46 53 66 72 79 89
- */
- /**
- * 总结:
- * 快速排序是常用排序中效率最高的一种排序方式。
- * 但在应用中的一此特殊情况影响他的效率,这不是算法本身的问题,而是如果实现的问题。
- * 已经有很好的实现方式改变一些特殊情况性能下降的问题。
- */
- *
- * @author SunnyMoon
- */
- ////////////////////////////////////////////////////////////////////////////////
- /*******************************************************************************
- * 概念介绍:
- * *****************************************************************************
- *
- * 影响效率的最大障碍:
- * 对枢纽数据的选择是影响排序的效率。上一篇文章选择枢纽数据为数组的最后一个元素,
- * 这么选择只是为方便,然而却造成了特殊情况时效率极度下降,降到O(n2)。这种特情况就是当数据为逆序的时候。
- * 如果改变特殊情况给快速排序带来的致命影响呢,这将在这一专题中简要介绍。
- *
- * 三数据项取中划分:
- * 目前已经有很多选择更好的枢纽的方法,选择任意一个数据项作为枢纽是一种简单的方法,便是
- * 正如前文看到的那样,在特殊情况下性能很低。从理论上讲实际使数组中值数据是最理想的枢纽
- * 选择,是效率最高的方式,但实际上这个过程比排序本身需要更长的时间,因此这是不可行的。
- * 一种折衷的方法,选择数组里的第一个,最后一个以及中间位置的数据中选择一个中间值,并设
- * 置此数据项为枢纽,这种选择枢纽的方式为三数据项选中。
- * 查找三个数据项的中值数据项比查找所有数据项中值快很多,在大多数通常情况下这种方法是又
- * 快又有效的方法。但是有很特殊的数据排列命名三数据项先中的方法也很低效。
- *
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * 定义一个数组类,封装了对自身数据的排序。
- */
- class ArrayIns {
- private long[] theArray;
- private int nElems;
- public ArrayIns(int max) {
- theArray = new long[max];
- nElems = 0;
- }
- public void insert(long value) {
- theArray[nElems] = value;
- nElems++;
- }
- public void display() {
- System.out.print("A=");
- for (int j = 0; j < nElems; j++) {
- System.out.print(theArray[j] + " ");
- }
- System.out.println("");
- }
- /**
- * 快速排序主方法
- */
- public void quickSort() {
- recQuickSort(0, nElems - 1);
- }
- /**
- * 递归调用快速的排序核心方法
- * @param left
- * @param right
- */
- public void recQuickSort(int left, int right) {
- int size = right-left + 1;
- if (size <= 3) {
- manualSort(left, right);
- } else {
- long median = medianOf3(left, right);//三数取中值
- int partition=partitionIt(left,right,median);//三数据取中值作为枢纽进行划分
- recQuickSort(left, partition - 1);//递归排序数组左子元素
- recQuickSort(partition + 1,right);//递归排序数据左子元素
- }
- }
- /**
- * 三数据选中
- * @param left
- * @param right
- * @return
- */
- public long medianOf3(int left, int right) {
- int center = (left + right) / 2;
- if (theArray[left] > theArray[center]) {
- swap(left, center);
- }
- if (theArray[left] > theArray[right]) {
- swap(left, right);
- }
- if (theArray[center] > theArray[right]) {
- swap(center, right);
- }
- swap(center, right - 1);
- return theArray[right - 1];
- }
- /**
- * 交换数据
- * @param dex1
- * @param dex2
- */
- public void swap(int dex1, int dex2) {
- long temp = theArray[dex1];
- theArray[dex1] = theArray[dex2];
- theArray[dex2] = temp;
- }
- /**
- * 根据三数据选中进行划分
- * @param left
- * @param right
- * @param pivot
- * @return
- */
- public int partitionIt(int left, int right, long pivot) {
- int leftPtr = left;//定位到最左元素
- int rightPtr = right-1;//定位到枢纽
- while (true) {
- while (theArray[++leftPtr] < pivot);//寻找大于枢纽元素
- while (theArray[--rightPtr] > pivot);//寻找小于枢纽元素
- if (leftPtr >= rightPtr) {//确定划分结束退出循环
- break;
- } else {
- swap(leftPtr, rightPtr);//交换需划分的元素
- }
- }
- swap(leftPtr, right-1);//恢复枢纽到正确位置
- return leftPtr;//返回枢纽
- }
- /**
- * 手动排序
- * @param left
- * @param right
- */
- public void manualSort(int left, int right) {
- int size = right-left + 1;
- if (size <= 1) {//当元素为1时直接返回
- return;
- }
- if (size == 2) {
- if (theArray[left] > theArray[right]) {//当元素为2时排序
- swap(left, right);
- }
- return;
- } else {//当元素为3时排序
- if (theArray[left] > theArray[right - 1]) {
- swap(left, right - 1);
- }
- if (theArray[left] > theArray[right]) {
- swap(left, right);
- }
- if (theArray[right - 1] > theArray[right]) {
- swap(right - 1, right);
- }
- }
- }
- }
- /**
- * 主类
- */
- public class QuickSort2 {
- public static void main(String[] args) {
- int maxSize = 15;
- ArrayIns arr = new ArrayIns(maxSize);
- for (int j = 0; j < maxSize; j++) {//随机生机数据插入数组中
- long n = (int) (java.lang.Math.random() * 99);
- arr.insert(n);
- }
- System.out.println("显示排序前数据");
- arr.display();
- arr.quickSort();
- System.out.println("显示排序后数据");
- arr.display();
- }
- }
- /**
- *运行结果:
- *显示排序前数据
- *A=38 62 44 89 50 91 36 60 47 22 83 7 33 31 38
- *显示排序后数据
- *A=7 22 31 33 36 38 38 44 47 50 60 62 83 89 91
- */
- /**
- * 总结:
- * 快速排序是常用排序中效率最高的一种排序方式。
- * 但在应用中的一此特殊情况影响他的效率,这不是算法本身的问题,而是如果实现的问题。
- * 三数据项选中方法很好的解决了这样的问题。
- */
- /**
- * @author SunnyMoon
- */
- /**********
- * 概念介绍:
- * ********
- *处理小划分:
- * 1. 如果使用三数据项取中值的方法取枢纽,这时快速排序算法不能执行三个或者小于三个数据项
- * 的划分规则,这时数字3就为排序算法的切割点。在上一篇中对三个或三个以下的数据排序时
- * 使用手动的方式排序,这种方式实践中不是最好的选择。
- * 2. 处理小划分的另一个选择是使用插入排序,当使用插入排序时可以将划分设定为10或其它任何
- * 数,试验不同的切割点的值以找到最佳的切割点,专家推荐使用9为切割点。对小的子数组使用
- * 插入排序被证时为最快的一种方法。将插入排序和快速排序相结合,可以把快速排序的性能发挥
- * 到极极,本篇程序使用该方法。
- * 4. 第三个选择是对数组整个使用快速排序不考虑处理小划分,当结束时数据基本有序了,然后
- * 使用插入排序对数据排序。因为插入排序对基本有序的数组执行效率很高,许多专家提倡使用
- * 这种方法,但是插入排序对小规模的数据排序很适合,随着数据规模的扩大性能下降明显。
- *
- * 消除递归:
- * 很多人提倡对快速排序算法进行修改,取消递归包括重写算法用栈实践。但是对于现在的
- * 系统来说消除递归所带来的改进不是很明显。
- */
- class ArrayIns {
- private long[] theArray;
- private int nElems;
- public ArrayIns(int max) {
- theArray = new long[max];
- nElems = 0;
- }
- public void insert(long value) {
- theArray[nElems] = value;
- nElems++;
- }
- public void display() {
- System.out.print("A=");
- for (int j = 0; j < nElems; j++) {
- System.out.print(theArray[j] + " ");
- }
- System.out.println("");
- }
- /**
- * 快速排序主方法
- */
- public void quickSort() {
- recQuickSort(0, nElems - 1);
- }
- /**
- * 递归调用快速的排序核心方法
- * @param left
- * @param right
- */
- public void recQuickSort(int left, int right) {
- int size = right - left + 1;
- if (size < 10) {
- insertionSort(left, right);
- } else {
- long median = medianOf3(left, right);//三数取中值
- int partition = partitionIt(left, right, median);//三数据取中值作为枢纽进行划分
- recQuickSort(left, partition - 1);//递归排序数组左子元素
- recQuickSort(partition + 1, right);//递归排序数据左子元素
- }
- }
- /**
- * 三数据选中
- * @param left
- * @param right
- * @return
- */
- public long medianOf3(int left, int right) {
- int center = (left + right) / 2;
- if (theArray[left] > theArray[center]) {
- swap(left, center);
- }
- if (theArray[left] > theArray[right]) {
- swap(left, right);
- }
- if (theArray[center] > theArray[right]) {
- swap(center, right);
- }
- swap(center, right - 1);
- return theArray[right - 1];
- }
- /**
- * 交换数据
- * @param dex1
- * @param dex2
- */
- public void swap(int dex1, int dex2) {
- long temp = theArray[dex1];
- theArray[dex1] = theArray[dex2];
- theArray[dex2] = temp;
- }
- /**
- * 根据三数据选中进行划分
- * @param left
- * @param right
- * @param pivot
- * @return
- */
- public int partitionIt(int left, int right, long pivot) {
- int leftPtr = left;//定位到最左元素
- int rightPtr = right - 1;//定位到枢纽
- while (true) {
- while (theArray[++leftPtr] < pivot);//寻找大于枢纽元素
- while (theArray[--rightPtr] > pivot);//寻找小于枢纽元素
- if (leftPtr >= rightPtr) {//确定划分结束退出循环
- break;
- } else {
- swap(leftPtr, rightPtr);//交换需划分的元素
- }
- }
- swap(leftPtr, right - 1);//恢复枢纽到正确位置
- return leftPtr;//返回枢纽
- }
- /**
- * 手动排序
- * @param left
- * @param right
- */
- public void insertionSort(int left, int right) {
- int in, out;//in为内循环指针,out为外循环指针
- for (out = left + 1; out <= right; out++) {
- long temp = theArray[out];//移除当前标记元素到临时变量
- in = out;//定位内循环
- while (in > left && theArray[in - 1] >= temp) {//满足条件时向左移动元素
- theArray[in] = theArray[in - 1];
- --in;
- }
- theArray[in] = temp;//插入当前标记元素到正确位置
- }
- }
- }
- /**
- * 主类
- */
- public class QuickSort3 {
- public static void main(String[] args) {
- int maxSize = 10;
- ArrayIns arr = new ArrayIns(maxSize);
- for (int j = 0; j < maxSize; j++) {//随机生机数据插入数组中
- long n = (int) (java.lang.Math.random() * 99);
- arr.insert(n);
- }
- System.out.println("显示排序前数据");
- arr.display();
- arr.quickSort();
- System.out.println("显示排序后数据");
- arr.display();
- }
- }
- /**
- * 运行结果:
- * 显示排序前数据
- * A=56 14 50 18 82 58 30 50 60 1
- * 显示排序后数据
- * A=1 14 18 30 50 50 56 58 60 82
- */
- /**
- * 总结:
- * 快速排序是常用排序中效率最高的一种排序方式。
- * 但在应用中的一此特殊情况影响他的效率,这不是算法本身的问题,而是如果实现的问题。
- * 三数据项选中方法很好的解决了这样的问题。三数据选中方法不是最好的选择,可以将插入排序
- * 与快速排序相结合的方式解决这样的问题,使快速排序算法发挥到极致。
- */