java一些必會演算法
1.大O表示法:粗略的量度方法即演算法的速度是如何與資料項的個數相關的
演算法 大O表示法表示的執行時間
線性查詢 O(N)
二分查詢 O(logN)
無序陣列的插入 O(1)
有序陣列的插入 O(N)
無序陣列的刪除 O(N)
有序陣列的刪除 O(N)
O(1)是最優秀的,O(logN)良好,O(N)還可以,O(N2)稍差(在冒泡法中見到)
2. 排序
public class JWzw { //插入排序 public void insertArray(Integer[] in ) { int tem = 0; int num = 0; int upnum = 0; for (int i = 0; i < in .length; i++) { for (int j = i - 1; j >= 0; j--) { num++; if ( in [j + 1] < in [j]) { tem = in [j + 1]; in [j + 1] = in [j]; in [j] = tem; upnum++; } else { break; } } } for (int i = 0; i < in .length; i++) { System.out.print( in [i]); if (i < in .length - 1) { System.out.print(","); } } System.out.println(); System.out.println("插入排序迴圈次數:" + num); System.out.println("移動次數:" + upnum); System.out.print("\n\n\n"); } //選擇排序 public void chooseArray(Integer[] in ) { int tem = 0; int num = 0; int upnum = 0; for (int i = 0; i < in .length; i++) { for (int j = 0; j < in .length - 1; j++) { num++; if ( in [j + 1] < in [j]) { tem = in [j + 1]; in [j + 1] = in [j]; in [j] = tem; upnum++; } } } for (int i = 0; i < in .length; i++) { System.out.print( in [i]); if (i < in .length - 1) { System.out.print(","); } } System.out.println(); System.out.println("選擇排序迴圈次數:" + num); System.out.println("移動次數:" + upnum); System.out.print("\n\n\n"); } //氣泡排序 public void efferArray(Integer[] in ) { int tem = 0; int num = 0; int upnum = 0; for (int i = 0; i < in .length; i++) { for (int j = i; j < in .length - 1; j++) { num++; if ( in [j + 1] < in [i]) { tem = in [j + 1]; in [j + 1] = in [i]; in [i] = tem; upnum++; } } } for (int i = 0; i < in .length; i++) { System.out.print( in [i]); if (i < in .length - 1) { System.out.print(","); } } System.out.println(); System.out.println("氣泡排序迴圈次數:" + num); System.out.println("移動次數:" + upnum); System.out.print("\n\n\n"); } //列印乘法口訣 public void printMulti() { for (int j = 1; j < 10; j++) { for (int i = 1; i <= j; i++) { System.out.print(i + " * " + j + " = " + j * i + "\t"); } System.out.print("\t\n"); } System.out.print("\n\n\n"); } //列印N * 1 + N * 2 + N * 3 =num的所有組合 public void printNumAssemble(int num) { for (int i = 0; i < num + 1; i++) { for (int j = 0; j < num / 2 + 1; j++) { for (int in = 0; in < num / 3 + 1; in ++) { if (i * 1 + j * 2 + in * 3 == num) { System.out.println("小馬" + i + ",\t中馬" + j + ",\t大馬" + in ); } } } } } /** * @param args */ public static void main(String[] args) { JWzw jwzw = new JWzw(); int num = 3; jwzw.printMulti(); //列印乘法口訣 jwzw.printNumAssemble(100); //列印N * 1 + N * 2 + N * 3 =num的所有組合 Integer in [] = { 8, 89, 5, 84, 3, 45, 12, 33, 77, 98, 456, 878, 654, 213, 897 }; jwzw.efferArray( in ); //氣泡排序 Integer in1[] = { 8, 89, 5, 84, 3, 45, 12, 33, 77, 98, 456, 878, 654, 213, 897 }; jwzw.insertArray(in1); //插入排序 Integer in2[] = { 8, 89, 5, 84, 3, 45, 12, 33, 77, 98, 456, 878, 654, 213, 897 }; jwzw.chooseArray(in2); //選擇排序 //int i = num++; //System.out.println(i); System.out.println(1000 >> 2); } }
3. 優先順序佇列
class PriorityQueue { private long[] a = null; private int nItems = 0; private int maxSize = 0; public PriorityQueue(int maxSize) { a = new long[maxSize]; this.maxSize = maxSize; nItems = 0; } public void insert(long l) { //優先順序佇列的插入不是隊尾,而是選擇一個合適的按照某種順序插入的 //當佇列長度為0時,如下 //不為0時,將所有比要插入的數小的資料後移,這樣大的數就在佇列的頭部了 int i = 0; if (nItems == 0) { a[0] = l; } else { for (i = nItems - 1; i >= 0; i--) { if (l < a[i]) a[i + 1] = a[i]; else break; } a[i + 1] = l; } nItems++; } public long remove() { //移出的是陣列最上端的數,這樣減少陣列元素的移動 return a[--nItems]; } public boolean isEmpty() { return (nItems == 0); } public boolean isFull() { return (nItems == maxSize); } public int size() { return nItems; } } public class duilie { // 佇列體類 private duilie s; private String data; duilie(String data) { this.data = data; } public String getData() { return data; } public void setData(String data) { this.data = data; } public duilie getS() { return s; } public void setS(duilie s) { this.s = s; } } public class duiliecz { // 佇列操作類 /** * @param args */ private int i = 0; // 佇列長 private duilie top = new duilie(""); // 佇列頭 private duilie end = new duilie(""); // 佇列尾 public void add(String s) { // 新增佇列 duilie m = new duilie(s); if (i != 0) { m.setS(top.getS()); top.setS(m); } else { top.setS(m); end.setS(m); } i++; }
4. 佇列
public void del() { // 刪除隊尾
if (i == 0) {
return;
} else if (i == 1) {
top.setS(null);
end.setS(null);
} else {
duilie top1 = new duilie(""); // 佇列底查詢用快取
top1.setS(top.getS());
while (!top1.getS().getS().equals(end.getS())) {
top1.setS(top1.getS().getS());
}
end.setS(top1.getS());
}
i--;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
duiliecz m = new duiliecz();
m.add("1");
m.add("2");
m.add("3");
m.add("4");
for (int n = 0; n < 4; n++) {
m.del();
}
}
public int getI() {
return i;
}
public duilie getEnd() {
return end;
}
public duilie getTop() {
return top;
}
}
5. 棧
public class Stack {
int[] arr;
int len = 0;
public Stack() {
arr = new int[100];
}
public Stack(int n) {
arr = new int[n];
}
public int size() {
return len + 1;
}
// 擴大陣列
public void resize() {
int[] b = new int[arr.length * 2];
System.arraycopy(arr, 0, b, 0, arr.length);
arr = b;
}
public void show() {
for (int i = 0; i < len; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 進棧
public void push(int a) {
if (len >= arr.length) resize();
arr[len] = a;
len++;
}
// 出棧
public int pop() {
if (len == 0) {
System.out.println();
System.out.println("stack is empty!");
return -1;
}
int a = arr[len - 1];
arr[len - 1] = 0;
len--;
return a;
}
}
6. 連結串列
class Node {
Object data;
Node next;
public Node(Object data) {
setData(data);
}
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
}
class Link {
Node head;
int size = 0;
public void add(Object data) {
Node n = new Node(data);
if (head == null) {
head = n;
} else {
Node current = head;
while (true) {
if (current.next == null) {
break;
}
current = current.next;
}
current.next = n;
}
size++;
}
public void show() {
Node current = head;
if (current != null) {
while (true) {
System.out.println(current);
if (current == null) {
break;
}
current = current.next;
}
} else {
System.out.println("link is empty");
}
}
public Object get(int index) {
// ....
}
public int size() {
return size;
}
}
7. 單鏈表
class Node // 節點類,單鏈表上的節點
{
String data; // 資料域,存放String類的資料
Node next; // 指向下一個節點
Node(String data) {
this.data = data; // 建構函式
}
String get() {
return data; // 返回資料
}
}
class MyLinkList // 連結串列類
{
Node first; // 頭節點
int size; // 連結串列長度
MyLinkList(String arg[]) {
// Node first = new Node("head");//生成頭節點
first = new Node("head"); // J.F. 這裡不需要定義區域性變數 first
// 如果定義了局部變數,那成員變數 first 就一直沒有用上
// 所以,它一直為空
size = 0;
Node p = first;
for (int i = 0; i < arg.length; i++) // 將arg陣列中的元素分別放入連結串列中
{
Node q = new Node(arg[i]);
q.next = p.next; // 每一個節點存放一個arg陣列中的元素
p.next = q;
p = p.next;
size++;
}
}
MyLinkList() // 無引數建構函式
{
// Node first = new Node("head");
first = new Node("head"); // J.F. 這裡犯了和上一個構造方法同樣的錯誤
size = 0;
}
int size() // 返回連結串列長度
{
return size;
}
void insert(Node a, int index) // 將節點a 插入連結串列中的第index個位置
{
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next; // 找到插入節點的前一節點
}
a.next = temp.next; // 插入節點
temp.next = a;
size++;
}
Node del(int index) // 刪除第index個節點,並返回該值
{
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next; // 找到被刪除節點的前一節點
}
Node node = temp.next;
temp.next = node.next;
size--; // 刪除該節點,連結串列長度減一
return node;
}
void print() // 在螢幕上輸出該連結串列(這段程式總是出錯,不知道錯在哪裡)
{
Node temp = first;
for (int i = 1; i < size; i++) // 將各個節點分別在螢幕上輸出
{
temp = temp.next;
System.out.print(temp.get() + "->");
}
}
void reverse() // 倒置該連結串列
{
for (int i = 0; i < size; i++) {
insert(del(size - 1), 0); // 將最後一個節點插入到最前
// J.F. 最後一個節點的 index 應該是 size - 1
// 因為第一個節點的 index 是 0
}
}
String get(int index) // 查詢第index個節點,返回其值
{
if (index >= size) {
return null;
}
Node temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next; // 找到被查詢節點的前一節點
}
return temp.next.get();
}
}
class MyStack // 堆疊類,用單鏈表實現
{
MyLinkList tmp;
Node temp;
MyStack() {
// MyLinkList tmp = new MyLinkList();
tmp = new MyLinkList(); // J.F. 和 MyLinkList 構造方法同樣的錯誤
}
void push(String a) // 壓棧,即往連結串列首部插入一個節點
{
Node temp = new Node(a);
tmp.insert(temp, 0);
}
String pop() // 出棧,將連結串列第一個節點刪除
{
Node a = tmp.del(0);
return a.get();
}
int size() {
return tmp.size();
}
boolean empty() // 判斷堆疊是否為空
{
if (tmp.size() == 0) return false;
else return true;
}
}
public class MyLinkListTest // 測試程式部分
{
public static void main(String arg[]) // 程式入口
{
if ((arg.length == 0) || (arg.length > 10)) System.out.println("長度超過限制或者缺少引數");
else {
MyLinkList ll = new MyLinkList(arg); // 建立一個連結串列
ll.print(); // 先輸出該連結串列(執行到這一步丟擲異常)
ll.reverse(); // 倒置該連結串列
ll.print(); // 再輸出倒置後的連結串列
String data[] = new String[10];
int i;
for (i = 0; i < ll.size(); i++) {
data[i] = ll.get(i); // 將連結串列中的資料放入陣列
}
// sort(data);// 按升序排列data中的資料(有沒有現成的排序函式?)
for (i = 0; i < ll.size(); i++) {
System.out.print(data[i] + ";"); // 輸出陣列中元素
}
System.out.println();
MyStack s = new MyStack(); // 建立堆疊例項s
for (i = 0; i < ll.size(); i++) {
s.push(data[i]); // 將陣列元素壓棧
}
while (!s.empty()) {
System.out.print(s.pop() + ";"); // 再將堆疊裡的元素彈出
}
}
}
}
8. 雙端連結串列
class Link {
public int iData = 0;
public Link next = null;
public Link(int iData) {
this.iData = iData;
}
public void display() {
System.out.print(iData + " ");
}
}
class FirstLastList {
private Link first = null;
private Link last = null;
public FirstLastList() {
first = null;
last = null;
}
public void insertFirst(int key) {
Link newLink = new Link(key);
if (this.isEmpty()) last = newLink;
newLink.next = first;
first = newLink;
}
public void insertLast(int key) {
Link newLink = new Link(key);
if (this.isEmpty()) first = newLink;
else last.next = newLink;
last = newLink;
}
public Link deleteFirst() {
Link temp = first;
if (first.next == null) last = null;
first = first.next;
return temp;
}
public boolean isEmpty() {
return (first == null);
}
public void displayList() {
System.out.print("List (first-->last): ");
Link current = first;
while (current != null) {
current.display();
current = current.next;
}
System.out.println("");
}
}
class FirstLastListApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
FirstLastList theList = new FirstLastList();
theList.insertFirst(22); // insert at front
theList.insertFirst(44);
theList.insertFirst(66);
theList.insertLast(11); // insert at rear
theList.insertLast(33);
theList.insertLast(55);
theList.displayList(); // display the list
theList.deleteFirst(); // delete first two items
theList.deleteFirst();
theList.displayList(); // display again
}
}
9. 有序連結串列
package arithmetic;
class Link {
public int iData = 0;
public Link next = null;
public Link(int iData) {
this.iData = iData;
}
public void display() {
System.out.print(iData + " ");
}
}
class SortedList {
private Link first = null;
public SortedList() {
first = null;
}
public void insert(int key) {
Link newLink = new Link(key);
Link previous = null;
Link current = first;
// while的第一個條件是沒有到達連結串列的尾端,第二個是按順序找到一個合適的位置
while (current != null && key > current.iData) {
previous = current;
current = current.next;
}
// 如果是空表或者要插入的元素最小,則在表頭插入key
if (current == first) first = newLink;
else previous.next = newLink;
newLink.next = current;
}
/**
* 刪除表頭的節點
*
* @return 要刪除的節點
*/
public Link remove() {
Link temp = first;
first = first.next;
return temp;
}
public boolean isEmpty() {
return (first == null);
}
public void displayList() {
System.out.print("List (first-->last): ");
Link current = first; // start at beginning of list
while (current != null) // until end of list,
{
current.display(); // print data
current = current.next; // move to next link
}
System.out.println("");
}
}
class SortedListApp {
public static void main(String[] args) { // create new list
SortedList theSortedList = new SortedList();
theSortedList.insert(20); // insert 2 items
theSortedList.insert(40);
theSortedList.displayList(); // display list
theSortedList.insert(10); // insert 3 more items
theSortedList.insert(30);
theSortedList.insert(50);
theSortedList.displayList(); // display list
theSortedList.remove(); // remove an item
theSortedList.displayList(); // display list
}
}
10. 雙向連結串列
class Link {
// 雙向連結串列,有兩個指標,一個向前,一個向後
public int iData = 0;
public Link previous = null;
public Link next = null;
public Link(int iData) {
this.iData = iData;
}
public void display() {
System.out.print(iData + " ");
}
}
class DoublyLinked {
// 分別指向連結串列的表頭和表尾
private Link first = null;
private Link last = null;
public boolean isEmpty() {
return first == null;
}
/**
* 在表頭插入資料
*
* @param 要插入的節點的資料
*/
public void insertFirst(int key) {
Link newLink = new Link(key);
// 如果開始連結串列為空,則插入第一個資料後,last也指向第一個資料
if (this.isEmpty()) last = newLink;
else { // 表不為空的情況
first.previous = newLink;
newLink.next = first;
}
// 無論怎樣,插入後都的讓first重新指向第一個節點
first = newLink;
}
public void insertLast(int key) { // 在尾端插入資料,同上
Link newLink = new Link(key);
if (this.isEmpty()) first = newLink;
else {
last.next = newLink;
newLink.previous = last;
}
last = newLink;
}
/**
* 在指定的節點後插入資料
*
* @param key
* 指定的節點的值
* @param iData
* 要插入的資料
* @return 是否插入成功
*/
public boolean insertAfter(int key, int iData) {
Link newLink = new Link(key);
Link current = first;
// 從first開始遍歷,看能否找到以key為關鍵字的節點
while (current.iData != key) {
current = current.next;
// 若能找到就跳出迴圈,否則返回false,插入失敗
if (current == null) return false;
}
// 如果插入點在last的位置
if (current == last) {
last = newLink;
} else { // 非last位置,交換各個next和previous的指標
newLink.next = current.next;
current.next.previous = newLink;
}
current.next = newLink;
newLink.previous = current;
return true;
}
/**
* 刪除表頭的節點
*
* @return
*/
public Link deleteFirst() {
Link temp = first;
// 如果表中只有一個元素,刪除後則為空表,所以last=null
if (first.next == null) last = null;
else
// 否則,讓第二個元素的previous=null
first.next.previous = null;
// 刪除頭指標,則first指向原來的second
first = first.next;
return temp;
}
public Link deleteLast() { // 同上
Link temp = last;
if (last.previous == null) first = null;
else last.previous.next = null;
last = last.previous;
return temp;
}
public Link deleteKey(int key) {
Link current = first;
// 遍歷整個連結串列查詢對應的key,如果查到跳出迴圈,否則...
while (current.iData != key) {
current = current.next;
// ...否則遍歷到表尾,說明不存在此key,返回null,刪除失敗
if (current == null) return null;
}
if (current == first) first = first.next;
else current.previous.next = current.next;
if (current == last) last = last.previous;
else current.next.previous = current.previous;
return current;
}
public void displayForward() {
Link current = first;
while (current != null) {
current.display();
current = current.next;
}
System.out.println();
}
public void displayBackward() {
Link current = last;
while (current != null) {
current.display();
current = current.previous;
}
System.out.println();
}
}
class DoublyLinkedApp {
public static void main(String[] args) { // make a new list
DoublyLinked theList = new DoublyLinked();
theList.insertFirst(22); // insert at front
theList.insertFirst(44);
theList.insertFirst(66);
theList.insertLast(11); // insert at rear
theList.insertLast(33);
theList.insertLast(55);
theList.displayForward(); // display list forward
theList.displayBackward(); // display list backward
theList.deleteFirst(); // delete first item
theList.deleteLast(); // delete last item
theList.deleteKey(11); // delete item with key 11
theList.displayForward(); // display list forward
theList.insertAfter(22, 77); // insert 77 after 22
theList.insertAfter(33, 88); // insert 88 after 33
theList.displayForward(); // display list forward
}
}
11. 實現二叉樹前序遍歷迭代器
class TreeNode這個類用來宣告樹的結點,其中有左子樹、右子樹和自身的內容。
class MyTree這個類用來宣告一棵樹,傳入根結點。這裡設計的比較簡單
class TreeEum這個類是樹的迭代器,通過 MyTree類的方法獲取,這裡主要就是設計它了。程式碼如下:
//TreeNode類,使用了泛型,由於比較簡單,考試.大提示不作解釋
class TreeNode < E > {
E node;
private TreeNode < String > left;
private TreeNode < String > right;
public TreeNode(E e) {
this(e, null, null);
}
public TreeNode(E e, TreeNode < String > left, TreeNode < String > right) {
this.node = e;
this.left = left;
this.right = right;
}
public TreeNode < String > left() {
return left;
}
public TreeNode < String > right() {
return right;
}
}
// MyTree類,沒什麼功能,傳入根結點構造,getEnumerator()方法獲取迭代器。
class MyTree {
TreeNode < String > root;
public MyTree(TreeNode < String > root) {
this.root = root;
}
public TreeEnum getEnumerator() {
return new TreeEnum(root);
}
}
// 這個類為迭代器,有詳細解釋,相信各位能看懂。在棧中用了兩次泛型。
import java.util.Stack;
public class TreeEnum {
private TreeNode < String > root;
private Stack < TreeNode < String >> store; /* 儲存遍歷左子樹但未遍歷右子樹的結點 */
private TreeNode < String > next;
public TreeEnum(TreeNode < String > root) {
this.root = root;
store = new Stack < TreeNode < String >> ();
next = root;
}
public TreeNode < String > next() {
TreeNode < String > current = next;
if (next != null) {
/* 如果當前結點的左子樹不為空,則遍歷左子樹,並標記當前結點未遍歷右子樹 */
if (next.left() != null) {
store.push(next);
next = next.left();
}
// 如果當前結點的左子樹為空,則遍歷右子樹
else if (next.right() != null) {
next = next.right();
}
/* 如果當前結點為葉子,則找未遍歷右子樹的結點並且遍歷它的右子樹 */
else {
if (!store.empty()) /* 判斷是否還有結點的右子樹未遍歷 */ {
TreeNode < String > tmp = store.pop();
/* 如果有未遍歷右子樹的結點,但它的右子樹為空,且還有結點的右子樹未遍歷, */
/* 則一直往上取,直到取到未遍歷右子樹且右子樹不為空的結點,遍歷它的右子樹. */
while ((tmp.right() == null) && !store.empty()) {
tmp = store.pop();
}
next = tmp.right();
}
else {
/* 如果沒有哪個結點右子樹未遍歷,則表示沒有下一個結點了,設定next為null */
next = null;
}
}
}
return current;
}
public boolean hasMoreElement() {
return next != null;
}
} 下面寫個測試類,不作解釋,相信大家看得懂
public class TreeReader {
public static void main(String[] args) {
TreeNode < String > n1 = new TreeNode < String > ("n1");
TreeNode < String > n2 = new TreeNode < String > ("n2");
TreeNode < String > n3 = new TreeNode < String > ("n3");
TreeNode < String > n4 = new TreeNode < String > ("n4");
TreeNode < String > n5 = new TreeNode < String > ("n5");
TreeNode < String > n6 = new TreeNode < String > ("n6", null, n1);
TreeNode < String > n7 = new TreeNode < String > ("n7", n2, null);
TreeNode < String > n8 = new TreeNode < String > ("n8", n7, null);
TreeNode < String > n9 = new TreeNode < String > ("n9", null, n5);
TreeNode < String > n10 = new TreeNode < String > ("n10", n4, n9);
TreeNode < String > n11 = new TreeNode < String > ("n11", n6, n8);
TreeNode < String > n12 = new TreeNode < String > ("n12", n3, n10);
TreeNode < String > root = new TreeNode < String > ("root", n11, n12);
MyTree tree = new MyTree(root);
TreeEnum eum = tree.getEnumerator();
while (eum.hasMoreElement()) {
System.out.print(eum.next().node + "--");
}
System.out.println("end");
}
}
12. 迭代器
package TreeIterator;
public interface Iterator {
public boolean hasNext();
public Object next();
}這個介面我們有
2個方法, hasNext()是否還有下一條資料, next返回具體的 Object這裡也就是樹。我們先不必要忙著做他的實現類,我們現在要來做的是這個容器(不是 JAVA中容器,與 arraylist什麼的無關),正所謂樹的容器是什麼,是山也!我們想想山應該具有什麼呢!?首先它要有種植樹的功能,這裡可以看作新增樹。我們可以想像山的功能是和樹相互關聯的,那麼他們之間是什麼關係呢,我們給他們一種聚合的關係,聚合的關係大家可以參考 UML圖,我在這裡給出它的一種程式表現形式。
package TreeIterator;
public class Hall {
Tree[] tree; // 這裡可以看作是聚合關係
private int index; // 指向Tree[]的標籤
public Hall(int maxNumber) {
tree = new Tree[maxNumber];
index = 0;
}
public void add(Tree tree) {
this.tree[index] = tree;
index++;
}
public Iterator connectIterator() {
return new TreeIterator(this);
}
}
這裡我們定義的山可以抽象出
Hall類來, Tree[] tree可以看作是山和樹之間的一種聚合關係。 add方法就是新增樹。問題來了,山和樹有了關係,那麼山和迭代器有什麼關係呢。它們之間肯定有一種關係。我們有了這個容器(山),就要把這個容器來實現迭代的方法: hasNext()和 Next().恩這裡我們可以看出,山和迭代器之間也是一種關聯關係。我們就把它看成是一種聚合關係(TIP:聚合關係一種特殊的關聯關係)。我們可以通過一個 connectIterator方法來連結山和迭代器,接下來我們要去做一個具體的迭代類,這個具體的類中間有了 hasNext()和 Next()的具體實現方法
package TreeIterator;
public class TreeIterator implements Iterator {
private int last = 0;
private Hall hall;
public TreeIterator(Hall hall) {
this.hall = hall;
}
public boolean hasNext() {
if (last < hall.tree.length) return true;
else return false;
}
public Tree next() {
Tree t = hall.tree[last];
last++;
return t;
}
}
這裡Hall hall就可以看作是一種關聯關係,我們要把山和迭代器聯絡起來就通過建構函式來實現, hasNext和 next實現方法就體現出來了有了山,有了迭代器,可是樹還沒有定義,不過這個樹的方法還很好解決!樹不關聯其他的事務,我們可以簡單的這麼寫:
package TreeIterator;
public class Tree {
private String name;
public Tree(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
好了似乎我們整個工程完工了,我們現在來模擬一下農民老大伯來種樹撒肥的過程;
package TreeIterator;
public class Pren {
public Pren() {}
public static void main(String[] args) {
Hall hall = new Hall(4);
hall.add(new Tree("蘋果樹"));
hall.add(new Tree("梨樹"));
hall.add(new Tree("橘子樹"));
hall.add(new Tree("鳳梨樹"));
for (Iterator i = hall.connectIterator(); i.hasNext();) {
String Type = ((Tree)(i.next())).getName();
if (Type == "蘋果樹") {
System.out.println("灑蘋果樹的農藥,");
}
if (Type == "梨樹") {
System.out.println("灑梨樹的農藥");
}
if (Type == "橘子樹") {
System.out.println("灑橘子樹的農藥,灑了也沒用,還沒到成熟日,現在沒結果實");
}
if (Type == "鳳梨樹") {
System.out.println("南風天,溼氣大,讓它爛在地裡吧");
}
}
}
}
種4個樹,山小而五臟俱全,更像一個土包,再要有樹才行啊,所以 4個樹的例項出現。好了接下來種樹,幾毫秒解決!山了有我們就要把山放到迭代器中間去了。遍歷這個山(容器)。聯想我們看看 arrayList中的迭代器模式是怎麼實現的!
ArrayList a = new ArrayList();
a.add("a1");
a.add("a2");
a.add("a3");
a.add("a4");
for (Iterator i = a.iterator(); i.hasNext();) {
System.out.println(i.next().toString());
}
13. 合併搜尋演算法
public class MergeSortArray {
private long[] theArray;
private int nElems;
public MergeSortArray(int max) {
theArray = new long[max];
nElems = 0;
}
public void insert(long value) {
theArray[nElems] = value; // insert it
nElems++; // increment size
}
public void display() {
for (int j = 0; j < nElems; j++) System.out.print(theArray[j] + " ");
System.out.println("");
}
public void mergeSort() {
long[] workSpace = new long[nElems];
recMergeSort(workSpace, 0, nElems - 1);
}
private void recMergeSort(long[] workSpace, int lowerBound, int upperBound) {
if (lowerBound == upperBound) // if range is 1,
return; // no use sorting
else { // find midpoint
int mid = (lowerBound + upperBound) / 2;
// sort low half
recMergeSort(workSpace, lowerBound, mid);
// sort high half
recMergeSort(workSpace, mid + 1, upperBound);
// merge them
merge(workSpace, lowerBound, mid + 1, upperBound);
}
}
private void merge(long[] workSpace, int lowPtr, int highPtr, int upperBound) {
int j = 0; // workspace index
int lowerBound = lowPtr;
int mid = highPtr - 1;
int n = upperBound - lowerBound + 1; // # of items
while (lowPtr <= mid && highPtr <= upperBound)
if (theArray[lowPtr] < theArray[highPtr]) workSpace[j++] = theArray[lowPtr++];
else workSpace[j++] = theArray[highPtr++];
while (lowPtr <= mid) workSpace[j++] = theArray[lowPtr++];
while (highPtr <= upperBound) workSpace[j++] = theArray[highPtr++];
for (j = 0; j < n; j++) theArray[lowerBound + j] = workSpace[j];
}
public static void main(String[] args) {
int maxSize = 100; // array size
MergeSortArray arr = new MergeSortArray(maxSize); // create the array
arr.insert(14);
arr.insert(21);
arr.insert(43);
arr.insert(50);
arr.insert(62);
arr.insert(75);
arr.insert(14);
arr.insert(2);
arr.insert(39);
arr.insert(5);
arr.insert(608);
arr.insert(36);
arr.display();
arr.mergeSort();
arr.display();
}
}
14. 遞迴
public class Recursion {
public static void main(String[] args) {
// TODO Auto-generated method stub
Recursion re = new Recursion();
System.out.println(re.RecursionNum(10));
}
public int RecursionNum(int num) {
if (num > 0) {
return num + RecursionNum(num - 1);
}
Else {
return 0;
}
}
}
15. 歸併排序
/**
* 歸併排序,要求待排序的陣列必須實現Comparable介面
*/
public class MergeSort implements SortStrategy {
private Comparable[] bridge;
/**
* 利用歸併排序演算法對陣列obj進行排序
*/
public void sort(Comparable[] obj) {
if (obj == null) {
throw new NullPointerException("The param can not be null!");
}
bridge = new Comparable[obj.length]; // 初始化中間陣列
mergeSort(obj, 0, obj.length - 1); // 歸併排序
bridge = null;
}
/**
* 將下標從left到right的陣列進行歸併排序
*
* @param obj
* 要排序的陣列的控制代碼
* @param left
* 要排序的陣列的第一個元素下標
* @param right
* 要排序的陣列的最後一個元素的下標
*/
private void mergeSort(Comparable[] obj, int left, int right) {
if (left < right) {
int center = (left + right) / 2;
mergeSort(obj, left, center);
mergeSort(obj, center + 1, right);
merge(obj, left, center, right);
}
}
/**
* 將兩個物件陣列進行歸併,並使歸併後為升序。歸併前兩個陣列分別有序
*
* @param obj
* 物件陣列的控制代碼
* @param left
* 左陣列的第一個元素的下標
* @param center
* 左陣列的最後一個元素的下標
* @param right
* 右陣列的最後一個元素的下標
*/
private void merge(Comparable[] obj, int left, int center, int right) {
int mid = center + 1;
int third = left;
int tmp = left;
while (left <= center && mid <= right) { // 從兩個陣列中取出小的放入中間陣列
if (obj[left].compareTo(obj[mid]) <= 0) {
bridge[third++] = obj[left++];
} else bridge[third++] = obj[mid++];
}
// 剩餘部分依次置入中間陣列
while (mid <= right) {
bridge[third++] = obj[mid++];
}
while (left <= center) {
bridge[third++] = obj[left++];
}
// 將中間陣列的內容拷貝回原陣列
copy(obj, tmp, right);
}
/**
* 將中間陣列bridge中的內容拷貝到原陣列中
*
* @param obj
* 原陣列的控制代碼
* @param left
* 要拷貝的第一個元素的下標
* @param right
* 要拷貝的最後一個元素的下標
*/
private void copy(Comparable[] obj, int left, int right) {
while (left <= right) {
obj[left] = bridge[left];
left++;
}
}
}
16. 希爾排序
間隔序列:
h = 3 * h + 1, h = (h - 1) / 3
public class ShellSort {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ShellSort ss = new ShellSort();
int num[] = {
546, 87, 21, 3124, 65, 2, 9, 3, 213, 54, 98, 23, 6, 4, 7,
8, 123, 872, 61, 5, 8954
};
ss.shellArray(num);
for (int i = 0; i < num.length; i++) {
System.out.println(num[i]);
}
}
public void shellArray(int[] num) {
int i = 1;
int tem, in ;
for (; i < num.length / 3;) {
i = 3 * i + 1;
}
for (; i >= 1;) {
for (int j = i; j < num.length; j++) {
tem = num[j]; in = j;
while ( in > i - 1 && num[ in -i] >= tem) {
num[ in ] = num[ in -i]; in = in -i;
}
num[ in ] = tem;
}
i = (i - 1) / 3;
}
}
}
17. 快速排序
class QuickSort {
private int[] data;
QuickSort(int[] data) {
this.data = data;
}
public void quickSort() {
recQuickSort(data, 0, data.length - 1);
}
private void recQuickSort(int[] data, int low, int high) {
// 設定兩個滑標
int lowCursor = low + 1;
int highCursor = high;
// 交換時的臨時變數
int temp = 0;
// 比較樞值,設為陣列的第一個值
int medi = data[low];
while (true) {
// 從低端開始查詢,確定大於數 data[low] 所在的位置
while (lowCursor < high && data[lowCursor] < medi) {
lowCursor++;
}
// 從高階開始查詢,確定小於數 data[low] 所在的位置。這裡要使用 >= 判斷確定小於值
while (highCursor > low && data[highCursor] >= medi) {
highCursor--;
}
// 兩遊標位置出現越界,退出迴圈
if (lowCursor >= highCursor) {
break;
}
// 交換 data[highCursor] 和 data[lowCursor] 位置資料
temp = data[highCursor];
data[highCursor] = data[lowCursor];
data[lowCursor] = temp;
}
// 由 while 迴圈退出條件可知:lowCursor > highCursor
// 當前 lowCursor 指向右側大於 data[low]的第一個位置;
// 而 highCursor 指向左側小於 data[low]的第一個位置,所以需要交換 data[low] 和
// data[highCursor]的值
data[low] = data[highCursor];
data[highCursor] = medi;
// 遞迴運算左半部分
if (low < highCursor) {
recQuickSort(data, low, highCursor);
}
// 遞迴運算右半部分
if (lowCursor < high) {
recQuickSort(data, lowCursor, high);
}
}
public void display() {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] data = new int[] {
43, 12, 32, 55, 33, 67, 54, 65, 43, 22, 66,
98, 74
};
QuickSort sort = new QuickSort(data);
sort.display();
sort.quickSort();
sort.display();
}
}
18. 二叉樹
//******************************************************************************************************//
//*****本程式包括簡單的二叉樹類的實現和前序,中序,後序,層次遍歷二叉樹演算法,*******//
//******以及確定二叉樹的高度,制定物件在樹中的所處層次以及將樹中的左右***********//
//******孩子節點對換位置,返回葉子節點個數刪除葉子節點,並輸出所刪除的葉子節點**//
//*******************************CopyRight By phoenix*******************************************//
//************************************Jan 12,2008*************************************************//
//****************************************************************************************************//
public class BinTree {
public final static int MAX = 40;
BinTree[] elements = new BinTree[MAX]; // 層次遍歷時儲存各個節點
int front; // 層次遍歷時隊首
int rear; // 層次遍歷時隊尾
private Object data; // 資料元數
private BinTree left, right; // 指向左,右孩子結點的鏈
public BinTree() {}
public BinTree(Object data) { // 構造有值結點
this.data = data;
left = right = null;
}
public BinTree(Object data, BinTree left, BinTree right) { // 構造有值結點
this.data = data;
this.left = left;
this.right = right;
}
public String toString() {
return data.toString();
}
// 前序遍歷二叉樹
public static void preOrder(BinTree parent) {
if (parent == null) return;
System.out.print(parent.data + " ");
preOrder(parent.left);
preOrder(parent.right);
}
// 中序遍歷二叉樹
public void inOrder(BinTree parent) {
if (parent == null) return;
inOrder(parent.left);
System.out.print(parent.data + " ");
inOrder(parent.right);
}
// 後序遍歷二叉樹
public void postOrder(BinTree parent) {
if (parent == null) return;
postOrder(parent.left);
postOrder(parent.right);
System.out.print(parent.data + " ");
}
// 層次遍歷二叉樹
public void LayerOrder(BinTree parent) {
elements[0] = parent;
front = 0;
rear = 1;
while (front < rear) {
try {
if (elements[front].data != null) {
System.out.print(elements[front].data + " ");
if (elements[front].left != null) elements[rear++] = elements[front].left;
if (elements[front].right != null) elements[rear++] = elements[front].right;
front++;
}
} catch (Exception e) {
break;
}
}
}
// 返回樹的葉節點個數
public int leaves() {
if (this == null) return 0;
if (left == null && right == null) return 1;
return (left == null ? 0 : left.leaves()) + (right == null ? 0 : right.leaves());
}
// 結果返回樹的高度
public int height() {
int heightOfTree;
if (this == null) return -1;
int leftHeight = (left == null ? 0 : left.height());
int rightHeight = (right == null ? 0 : right.height());
heightOfTree = leftHeight < rightHeight ? rightHeight : leftHeight;
return 1 + heightOfTree;
}
// 如果物件不在樹中,結果返回-1;否則結果返回該物件在樹中所處的層次,規定根節點為第一層
public int level(Object object) {
int levelInTree;
if (this == null) return -1;
if (object == data) return 1; // 規定根節點為第一層
int leftLevel = (left == null ? -1 : left.level(object));
int rightLevel = (right == null ? -1 : right.level(object));
if (leftLevel < 0 && rightLevel < 0) return -1;
levelInTree = leftLevel < rightLevel ? rightLevel : leftLevel;
return 1 + levelInTree;
}
// 將樹中的每個節點的孩子對換位置
public void reflect() {
if (this == null) return;
if (left != null) left.reflect();
if (right != null) right.reflect();
BinTree temp = left;
left = right;
right = temp;
}
// 將樹中的所有節點移走,並輸出移走的節點
public void defoliate() {
if (this == null) return;
// 若本節點是葉節點,則將其移走
if (left == null && right == null) {
System.out.print(this + " ");
data = null;
return;
}
// 移走左子樹若其存在
if (left != null) {
left.defoliate();
left = null;
}
// 移走本節點,放在中間表示中跟移走...
// innerNode += this + " ";
data = null;
// 移走右子樹若其存在
if (right != null) {
right.defoliate();
right = null;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
BinTree e = new BinTree("E");
BinTree g = new BinTree("G");
BinTree h = new BinTree("H");
BinTree i = new BinTree("I");
BinTree d = new BinTree("D", null, g);
BinTree f = new BinTree("F", h, i);
BinTree b = new BinTree("B", d, e);
BinTree c = new BinTree("C", f, null);
BinTree tree = new BinTree("A", b, c);
System.out.println("前序遍歷二叉樹結果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍歷二叉樹結果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("後序遍歷二叉樹結果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("層次遍歷二叉樹結果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的層次: " + tree.level("F"));
System.out.println("這棵二叉樹的高度: " + tree.height());
System.out.println("--------------------------------------");
tree.reflect();
System.out.println("交換每個節點的孩子節點後......");
System.out.println("前序遍歷二叉樹結果: ");
tree.preOrder(tree);
System.out.println();
System.out.println("中序遍歷二叉樹結果: ");
tree.inOrder(tree);
System.out.println();
System.out.println("後序遍歷二叉樹結果: ");
tree.postOrder(tree);
System.out.println();
System.out.println("層次遍歷二叉樹結果: ");
tree.LayerOrder(tree);
System.out.println();
System.out.println("F所在的層次: " + tree.level("F"));
System.out.println("這棵二叉樹的高度: " + tree.height());
}
}
經典演算法的Java實現
(1)河內塔問題:
說明:
河內之塔(Towers of Hanoi)是法國人M.Claus(Lucas)於1883年從泰國帶至法國的,河內為越戰時北越的首都,即現在的胡志明市;1883年法國數學家 Edouard Lucas曾提及這個故事,據說創世紀時Benares有一座波羅教塔,是由三支鑽石棒(Pag)所支撐,開始時神在第一根棒上放置64個由上至下依由小至大排列的金盤(Disc),並命令僧侶將所有的金盤從第一根石棒移至第三根石棒,且搬運過程中遵守大盤子在小盤子之下的原則,若每日僅搬一個盤子,則當盤子全數搬運完畢之時,此塔將毀損,而也就是世界末日來臨之時。
解法:
如果柱子標為ABC,要由A搬至C,在只有一個盤子時,就將它直接搬至C,當有兩個盤子,就將B當作輔助柱。
如圖所示:
事實上,若有n個盤子,則移動完畢所需之次數為2^n - 1,所以當盤數為64時,則所需次數為:264- 1 = 18446744073709551615 為5.05390248594782e+16年,也就是約5000世紀,如果對這數字沒什麼概念,就假設每秒鐘搬一個盤子好了,也要約5850億年左右。
實現:
//Java程式的實現
import java.io.*;
public class Hanoi {
public static void main(String args[]) throws IOException {
int n;
BufferedReader buf;
buf = new BufferedReader(new InputStreamReader(System. in ));
System.out.print("請輸入盤數:");
n = Integer.parseInt(buf.readLine());
Hanoi hanoi = new Hanoi();
hanoi.move(n, 'A', 'B', 'C');
}
public void move(int n, char a, char b, char c) {
if (n == 1) System.out.println("盤 " + n + " 由 " + a + " 移至 " + c);
else {
move(n - 1, a, c, b);
System.out.println("盤 " + n + " 由 " + a + " 移至 " + c);
move(n - 1, b, a, c);
}
}
}
(2)費式數列
說明:
Fibonacci為1200年代的歐洲數學家,在他的著作中曾經提到:“若有一隻免子每個月生一隻小免子,一個月後小免子也開始生產。起初只有一隻免子,一個月後就有兩隻免子,二個月後有三隻免子,三個月後有五隻免子(小免子投入生產)......”。
如果不太理解這個例子的話,舉個圖就知道了,注意新生的小免子需一個月成長期才會投入生產,類似的道理也可以用於植物的生長,這就是Fibonacci數列,一般習慣稱之為費氏數列,例如以下:
1、1 、2、3、5、8、13、21、34、55、89......
解法:
依說明,我們可以將費氏數列定義為以下:
fn = fn-1 + fn-2 if n > 2
fn = 1 if n = 0, 1
實現:
//Java程式的實現:
public class Fibonacci {
public static void main(String[] args) {
int[] fib = new int[20];
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i < fib.length; i++) fib[i] = fib[i - 1] + fib[i - 2];
for (int i = 0; i < fib.length; i++) System.out.print(fib[i] + " ");
System.out.println();
}
}
(3)巴斯卡(Pascal)三角形
說明:
巴斯卡(Pascal)三角形基本上就是在解 nCr ,因為三角形上的每一個數字各對應一個nCr,其中 n 為 row,而 r 為 column,如下:
0C0
1C0 1C1
2C0 2C1 2C2
3C0 3C1 3C2 3C3
4C0 4C1 4C2 4C3 4C4
對應的資料如下圖所示:
解法:
巴斯卡三角形中的 nCr 可以使用以下這個公式來計算,以避免階乘運算時的數值溢位:
nCr = [(n-r+1)*nCr-1]/r
nC0 = 1
實現:
//java實現
import java.awt.*;
import javax.swing.*;
public class Pascal extends JFrame {
public Pascal() {
setBackground(Color.white);
setTitle("巴斯卡三角形");
setSize(520, 350);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(700, 700);
setVisible(true);
}
private long combi(int n, int r) {
int i;
long p = 1;
for (i = 1; i <= r; i++) p = p * (n - i + 1) / i;
return p;
}
public void paint(Graphics g) {
g.setColor(Color.white);
g.clearRect(0, 0, getSize().width, getSize().height);
g.setColor(Color.red);
final int N = 12;
int n, r, t;
for (n = 0; n <= N; n++) {
for (r = 0; r <= n; r++) g.drawString(" " + combi(n, r), (N - n) * 20 + r * 40, n * 20 + 50);
}
}
public static void main(String args[]) {
Pascal frm = new Pascal();
}
}
(4)蒙地卡羅法求 PI
說明:
蒙地卡羅為摩洛哥王國之首都,該國位於法國與義大利國境,以賭博聞名。蒙地卡羅的基本原理為以亂數配合面積公式來進行解題,這種以機率來解題的方式帶有賭博的意味,雖然在精確度上有所疑慮,但其解題的思考方向卻是個值得學習的方式。
解法:
蒙地卡羅的解法適用於與面積有關的題目,例如求PI值或橢圓面積,這邊介紹如何求PI值;假設有一個圓半徑為1,所以四分之一圓面積就為PI,而包括此四分之一圓的正方形面積就為1,如下圖所示:
如果隨意的在正方形中投射飛標(點)好了,則這些飛標(點)有些會落於四分之一圓內,假設所投射的飛標(點)有n點,在圓內的飛標(點)有c點,則依比例來算,就會得到上圖中最後的公式。
至於如何判斷所產生的點落於圓內,很簡單,令亂數產生X與Y兩個數值,如果X^2+Y^2等於1就是落在圓內。
實現:
//java程式實現
public class PI {
public static void main(String[] args) {
final int N = 50000;
int sum = 0;
for (int i = 1; i < N; i++) {
double x = Math.random();
double y = Math.random();
if ((x * x + y * y) < 1) sum++;
}
System.out.println("PI = " + (double) 4 * sum / N);
}
}
(5)最大公因數、最小公倍數
說明:
解法:
最大公因數使用輾轉相除法來求,最小公倍數則由這個公式來求:
GCD * LCM = 兩數乘積
實現:
//java程式實現
import java.io.*;
public class GcdLcm {
public static int gcdOf(int m, int n) {
int r;
while (n != 0) {
r = m % n;
m = n;
n = r;
}
return m;
}
public static int lcmOf(int m, int n) {
return m * n / gcdOf(m, n);
}
public static void main(String[] args) throws IOException {
BufferedReader ln = new BufferedReader(new InputStreamReader(System. in ));
System.out.print("請輸入第一個數:");
int x = Integer.parseInt(ln.readLine());
System.out.print("請輸入第二個數:");
int y = Integer.parseInt(ln.readLine());
System.out.println("GCD of (" + x + "," + y + ")=" + GcdLcm.gcdOf(x, y));
System.out.println("LCM of (" + x + "," + y + ")=" + GcdLcm.lcmOf(x, y));
}
}
(6)阿姆斯壯數
說明:
在三位的整數中,例如153可以滿足13 + 53 + 33 = 153,這樣的數稱之為Armstrong數,試寫出一程式找出所有的三位數Armstrong數。
解法:
Armstrong數的尋找,其實就是在問如何將一個數字分解為個位數、十位數、百位數......,這隻要使用除法與餘數運算就可以了,例如輸入 input為abc,則:
a = input / 100
b = (input%100) / 10
c = input % 10
實現:
//java程式實現
public class Armstrong {
public static void main(String[] args) {
System.out.println("尋找Armstrong數:");
for (int i = 100; i <= 999; i++) {
int a = i / 100;
int b = (i % 100) / 10;
int c = i % 10;
if (a * a * a + b * b * b + c * c * c == i) System.out.print(i + " ");
}
System.out.println();
}
}
(7)最大訪客數
說明:
現將舉行一個餐會,讓訪客事先填寫到達時間與離開時間,為了掌握座位的數目,必須先估計不同時間的最大訪客數。
解法:
這個題目看似有些複雜,其實相當簡單,單就計算訪客數這個目的,同時考慮同一訪客的來訪時間與離開時間,反而會使程式變得複雜;只要將來訪時間與離開時間分開處理就可以了,假設訪客 i 的來訪時間為x[i],而離開時間為y[i]。
在資料輸入完畢之後,將x[i]與y[i]分別進行排序(由小到大),道理很簡單,只要先計算某時之前總共來訪了多少訪客,然後再減去某時之前的離開訪客,就可以輕易的解出這個問題
實現:
//java實現
import java.io.*;
import java.util.*;
public class MaxVisit {
public static int maxGuest(int[] x, int[] y, int time) {
int num = 0;
for (int i = 0; i < x.length; i++) {
if (time > x[i]) num++;
if (time > y[i]) num--;
}
return num;
}
public static void main(String[] args) throws IOException {
BufferedReader buf = new BufferedReader(new InputStreamReader(System. in ));
System.out.println("輸入來訪時間與離開時間(0~24):");
System.out.println("範例:10 15");
System.out.println("輸入-1結束");
java.util.ArrayList list = new ArrayList();
while (true) {
System.out.print(">>");
String input = buf.readLine();
if (input.equals("-1")) break;
list.add(input);
}
int[] x = new int[list.size()];
int[] y = new int[list.size()];
for (int i = 0; i < x.length; i++) {
String input = (String) list.get(i);
String[] strs = input.split(" ");
x[i] = Integer.parseInt(strs[0]);
y[i] = Integer.parseInt(strs[1]);
}
Arrays.sort(x);
Arrays.sort(y);
for (int time = 0; time < 25; time++) {
System.out.println(time + " 時的最大訪客數:" + MaxVisit.maxGuest(x, y, time));
}
}
}
(8)洗撲克牌(亂數排列)
說明:
洗撲克牌的原理其實與亂數排列是相同的,都是將一組數字(例如1~N)打亂重新排列,只不過洗撲克牌多了一個花色判斷的動作而已。
<