日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java一些必会算法(转自落尘曦的博客:http://blog.csdn.net/qq_23994787。 )

發布時間:2023/12/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java一些必会算法(转自落尘曦的博客:http://blog.csdn.net/qq_23994787。 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

經典算法的Java實現

(1)河內塔問題: 42

(2)費式數列 43

(3)巴斯卡(Pascal)三角形 44

(4)蒙地卡羅法求 PI 45

(5)最大公因數、最小公倍數 46

(6)阿姆斯壯數 47

(7)最大訪客數 48

(8)洗撲克牌(亂數排列) 49

(9)約瑟夫問題(Josephus Problem) 50

(10)排列組合 52

(11)得分排行 53

(12)選擇、插入、氣泡排序 55

(13)快速排序(一) 58

(14)快速排序(二) 60

(15)快速排序(三) 61

(16)合并排序 62

(17)基數排序 63

(18)循序查找法(使用衛兵) 65

(19)二分查找法 66

(20)插補查找法 67

(21)費式查找法 68

(22)稀疏矩陣 71

(23)多維矩陣轉一維矩陣 72

(24)上三角、下三角、對稱矩陣 73

(25)奇數魔方陣 75

(26)4N魔方陣 76

(27)2(2n+1)魔方陣 78

?

?

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)打亂重新排列,只不過洗撲克牌多了一個花色判斷的動作而已。

    解法:

    初學者通常會直接想到,隨機產生1~N的亂數并將之存入陣列中,后來產生的亂數存入陣列前必須先檢查陣列中是否已有重復的數字,如果有這個數就不存入,再重新產生下一個數,運氣不好的話,重復的次數就會很多,程式的執行速度就很慢了,這不是一個好方法。

    以1~52的亂數排列為例好了,可以將陣列先依序由1到52填入,然后使用一個回圈走訪陣列,并隨機產生1~52的亂數,將產生的亂數當作索引取出陣列值,并與目前陣列走訪到的值相交換,如此就不用擔心亂數重復的問題了,陣列走訪完畢后,所有的數字也就重新排列了。

    至于如何判斷花色?這只是除法的問題而已,取商數判斷花色,取余數判斷數字,您可以直接看程式比較清楚。

    ?

    實現:

  • //java實現

  • public class ShuffleCard {

  • public static void main(String args[]) {

  • final int N = 52;

  • int[] poker = new int[N + 1];

  • // 初始化陣列

  • for (int i = 1; i <= N; i++) poker[i] = i;

  • // 洗牌

  • for (int i = 1; i <= N; i++) {

  • int j = (int)(Math.random() * N);

  • if (j == 0) j = 1;

  • int tmp = poker[i];

  • poker[i] = poker[j];

  • poker[j] = tmp;

  • }

  • for (int i = 1; i <= N; i++) {

  • // 判斷花色

  • switch ((poker[i] - 1) / 13) {

  • case 0:

  • System.out.print("桃");

  • break;

  • case 1:

  • System.out.print("心");

  • break;

  • case 2:

  • System.out.print("磚");

  • break;

  • case 3:

  • System.out.print("梅");

  • break;

  • }

  • // 撲克牌數字

  • int remain = poker[i] % 13;

  • switch (remain) {

  • case 0:

  • System.out.print("K ");

  • break;

  • case 12:

  • System.out.print("Q ");

  • break;

  • case 11:

  • System.out.print("J ");

  • break;

  • default:

  • System.out.print(remain + " ");

  • break;

  • }

  • if (i % 13 == 0) System.out.println("");

  • }

  • }

  • }


  • ?

    (9)約瑟夫問題(Josephus Problem)

    說明:

    據說著名猶太歷史學家?Josephus有過以下的故事:在羅馬人占領喬塔帕特后,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧愿死也不要被敵人到,于是決定了一個自殺方式,41個人排成一個圓圈,由第1個人 開始報數,每報數到第3人該人就必須自殺,然后再由下一個重新報數,直到所有人都自殺身亡為止。

    然而Josephus 和他的朋友并不想遵從,Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,于是逃過了這場死亡游戲。

    解法:

    約瑟夫問題可用代數分析來求解,將這個問題擴大好了,假設現在您與m個朋友不幸參與了這個游戲,您要如何保護您與您的朋友?只要畫兩個圓圈就可以讓自己與朋友免于死亡游戲,這兩個圓圈內圈是排列順序,而外圈是自殺順序,如下圖所示:

    ?

    使用程式來求解的話,只要將陣列當作環狀來處理就可以了,在陣列中由計數1開始,每找到三個無資料區就填入一個計數,直而計數達41為止,然后將陣列由索引1開始列出,就可以得知每個位置的自殺順序,這就是約瑟夫排列,41個人而報數3的約琴夫排列如下所示:

    14 36 1 38 15 2 24 30 3 16 34 4 25 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23

    ?

    由上可知,最后一個自殺的是在第31個位置,而倒數第二個自殺的要排在第16個位置,之前的人都死光了,所以他們也就不知道約琴夫與他的朋友并沒有遵守游戲規則了。

    實現:

  • //java實現

  • public class Josephus {

  • public static int[] arrayOfJosephus(int number, int per) {

  • int[] man = new int[number];

  • for (int count = 1, i = 0, pos = -1; count <= number; count++) {

  • do {

  • pos = (pos + 1) % number; // 環狀處理

  • if (man[pos] == 0) i++;

  • if (i == per) { // 報數為3了

  • i = 0;

  • break;

  • }

  • } while (true);

  • man[pos] = count;

  • }

  • return man;

  • }

  • public static void main(String[] args) {

  • int[] man = Josephus.arrayOfJosephus(41, 3);

  • int alive = 3;

  • System.out.println("約琴夫排列:");

  • for (int i = 0; i < 41; i++) System.out.print(man[i] + " ");

  • System.out.println("\nL表示3個存活的人要放的位置:");

  • for (int i = 0; i < 41; i++) {

  • if (man[i] > (41 - alive)) System.out.print("L");

  • else System.out.print("D");

  • if ((i + 1) % 5 == 0) System.out.print(" ");

  • }

  • System.out.println();

  • }

  • }


  • ?

    (10)排列組合

    說明:

    將一組數字、字母或符號進行排列,以得到不同的組合順序,例如1 2 3這三個數的排列組合有:1 2 3、1 3 2、2 1 3、2 3 1、3 1 2、3 2 1。

    解法:

    可以使用遞回將問題切割為較小的單元進行排列組合,例如1 2 3 4的排列可以分為1 [2 3 4]、2 [1 3 4]、3 [1 2 4]、4 [1 2 3]進行排列,這邊利用旋轉法,先將旋轉間隔設為0,將最右邊的數字旋轉至最左邊,并逐步增加旋轉的間隔,例如:

    1 2 3 4 -> 旋轉1 -> 繼續將右邊2 3 4進行遞回處理

    2 1 3 4 -> 旋轉1 2 變為 2 1-> 繼續將右邊1 3 4進行遞回處理

    3 1 2 4 -> 旋轉1 2 3變為 3 1 2 -> 繼續將右邊1 2 4進行遞回處理

    4 1 2 3 -> 旋轉1 2 3 4變為4 1 2 3 -> 繼續將右邊1 2 3進行遞回處理

    ?

    實現:

  • //java實現

  • public class Permutation {

  • public static void perm(int[] num, int i) {

  • if (i < num.length - 1) {

  • for (int j = i; j <= num.length - 1; j++) {

  • int tmp = num[j];

  • // 旋轉該區段最右邊數字至最左邊

  • for (int k = j; k > i; k--) num[k] = num[k - 1];

  • num[i] = tmp;

  • perm(num, i + 1);

  • // 還原

  • for (int k = i; k < j; k++) num[k] = num[k + 1];

  • num[j] = tmp;

  • }

  • } else {

  • // 顯示此次排列

  • for (int j = 1; j <= num.length - 1; j++) System.out.print(num[j] + " ");

  • System.out.println();

  • }

  • }

  • public static void main(String[] args) {

  • int[] num = new int[4 + 1];

  • for (int i = 1; i <= num.length - 1; i++) num[i] = i;

  • perm(num, 1);

  • }

  • }


  • ?

    (11)得分排行

    說明:

    假設有一教師依學生座號輸入考試分數,現希望在輸入完畢后自動顯示學生分數的排行,當然學生的分數可能相同。

    ?

    解法:

    這個問題基本上要解不難,只要使用額外的一個排行陣列走訪分數陣列就可以了,直接使用下面的程式片段作說明:

    for(i = 0; i < count; i++) {

    ????juni[i] = 1;

    ????for(j = 0; j < count; j++) {

    ????????if(score[j] > score[i])

    ????????????juni[i]++;

    ???}

    }

    printf("得分\t排行\n");

    for(i = 0; i < count; i++)

    ????printf("%d\t%d\n", score[i], juni[i]);

    上面這個方法雖然簡單,但是反覆計算的次數是n^2,如果n值變大,那么運算的時間就會拖長;改變juni陣列的長度為n+2,并將初始值設定為0,如下所示:

    接下來走訪分數陣列,并在分數所對應的排行陣列索引元素上加1,如下所示:

    將排行陣列最右邊的元素設定為1,然后依序將右邊的元素值加至左邊一個元素,最后排行陣列中的「分數+1」」就是得該分數的排行,如下所示:

    這樣的方式看起來復雜,其實不過在計算某分數之前排行的人數,假設89分之前的排行人數為x人,則89分自然就是x+1了,這也是為什么排行陣列最右邊要設定為1的原因;如果89分有y人,則88分自然就是x+y+1,整個陣列右邊元素向左加的原因正是如此。

    如果分數有負分的情況,由于C/C++或Java等程式語言無法處理負的索引,所以必須加上一個偏移值,將所有的分數先往右偏移一個范圍即可,最后顯示的時候記得減回偏移值就可以了。

    實現:

    ?

  • //

  • import java.io.*;

  • public class ScoreRank {

  • public static void main(String[] args)

  • throws NumberFormatException, IOException {

  • final int MAX = 100;

  • final int MIN = 0;

  • int[] score = new int[MAX + 1];

  • int[] juni = new int[MAX + 2];

  • BufferedReader reader = new BufferedReader(new InputStreamReader(System. in ));

  • int count = 0;

  • do {

  • System.out.print("輸入分數,-1結束:");

  • score[count++] = Integer.parseInt(reader.readLine());

  • } while ((score[count - 1] != -1));

  • count--;

  • for (int i = 0; i < count; i++) juni[score[i]]++;

  • juni[MAX + 1] = 1;

  • for (int i = MAX; i >= MIN; i--) juni[i] = juni[i] + juni[i + 1];

  • System.out.println("得分\t排行");

  • for (int i = 0; i < count; i++) {

  • System.out.println(score[i] + "\t" + juni[score[i] + 1]);

  • }

  • }

  • }


  • ?

    (12)選擇、插入、氣泡排序

    說明:

    選擇排序(Selection sort)、插入排序(Insertion sort)與氣泡排序(Bubble sort)這三個排序方式是初學排序所必須知道的三個基本排序方式,它們由于速度不快而不實用(平均與最快的時間復雜度都是O(n2)),然而它們排序的方式確是值得觀察與探討的。

    解法:

    ?

    ?

    ① 選擇排序

    將要排序的對象分作兩部份,一個是已排序的,一個是未排序的,從后端未排序部份選擇一個最小值,并放入前端已排序部份的最后一個,例如:

    ?

    排序前:70 80 31 37 10 1 48 60 33 80

    ?

    [1] 80 31 37 10 70 48 60 33 80 選出最小值1

    [1 10] 31 37 80 70 48 60 33 80 選出最小值10

    [1 10 31] 37 80 70 48 60 33 80 選出最小值31

    [1 10 31 33] 80 70 48 60 37 80 ......

    [1 10 31 33 37] 70 48 60 80 80 ......

    [1 10 31 33 37 48] 70 60 80 80 ......

    [1 10 31 33 37 48 60] 70 80 80 ......

    [1 10 31 33 37 48 60 70] 80 80 ......

    [1 10 31 33 37 48 60 70 80] 80 ......

    ?

    ② 插入排序

    像是玩樸克一樣,我們將牌分作兩堆,每次從后面一堆的牌抽出最前端的牌,然后插入前面一堆牌的適當位置,例如:

    ?

    排序前:92 77 67 8 6 84 55 85 43 67

    ?

    [77 92] 67 8 6 84 55 85 43 67 將77插入92前

    [67 77 92] 8 6 84 55 85 43 67 將67插入77前

    [8 67 77 92] 6 84 55 85 43 67 將8插入67前

    [6 8 67 77 92] 84 55 85 43 67 將6插入8前

    [6 8 67 77 84 92] 55 85 43 67 將84插入92前

    [6 8 55 67 77 84 92] 85 43 67 將55插入67前

    [6 8 55 67 77 84 85 92] 43 67 ......

    [6 8 43 55 67 77 84 85 92] 67 ......

    [6 8 43 55 67 67 77 84 85 92] ......

    ?

    ③ 氣泡排序法

    顧名思義,就是排序時,最大的元素會如同氣泡一樣移至右端,其利用比較相鄰元素的方法,將大的元素交換至右端,所以大的元素會不斷的往右移動,直到適當的位置為止。

    ?

    基本的氣泡排序法可以利用旗標的方式稍微減少一些比較的時間,當尋訪完陣列后都沒有發生任何的交換動作,表示排序已經完成,而無需再進行之后的回圈比較與交換動作,例如:

    ?

    排序前:95 27 90 49 80 58 6 9 18 50

    ?

    27 90 49 80 58 6 9 18 50 [95] 95浮出?

    27 49 80 58 6 9 18 50 [90 95] 90浮出?

    27 49 58 6 9 18 50 [80 90 95] 80浮出?

    27 49 6 9 18 50 [58 80 90 95] ......

    27 6 9 18 49 [50 58 80 90 95] ......

    6 9 18 27 [49 50 58 80 90 95] ......

    6 9 18 [27 49 50 58 80 90 95] 由于接下來不會再發生交換動作,排序提早結束

    ?

    在上面的例子當中,還加入了一個觀念,就是當進行至i與i+1時沒有交換的動作,表示接下來的i+2至n已經排序完畢,這也增進了氣泡排序的效率。

    ?

    實現:

  • //Java程序實現

  • public class BasicSort {

  • public static void selectionSort(int[] number) {

  • for (int i = 0; i < number.length - 1; i++) {》》

  • int m = i;

  • for (int j = i + 1; j < number.length; j++)

  • if (number[j] < number[m]) m = j; === = if (i != m) swap(number, i, m);

  • }

  • }

  • public static void injectionSort(int[] number) {

  • for (int j = 1; j < number.length; j++) {

  • int tmp = number[j];

  • int i = j - 1;

  • while (tmp < number[i]) {

  • number[i + 1] = number[i];

  • i--;

  • if (i == -1) break;

  • }

  • number[i + 1] = tmp;

  • }

  • }

  • public static void bubbleSort(int[] number) {

  • boolean flag = true;

  • for (int i = 0; i < number.length - 1 && flag; i++) {

  • flag = false;

  • for (int j = 0; j < number.length - i - 1; j++) {

  • if (number[j + 1] < number[j]) {

  • swap(number, j + 1, j);

  • flag = true;

  • }

  • }

  • }

  • }

  • private static void swap(int[] number, int i, int j) {

  • int t;

  • t = number[i];

  • number[i] = number[j];

  • number[j] = t;

  • }

  • public static void main(String[] args) {

  • //測試:

  • int[] a = {

  • 10, 9, 1, 100, 20, 200, 39, 45, 23, 18, 2, 2, 15

  • };

  • //測試選擇排序:

  • System.out.println("選擇排序前:");

  • for (int x: a) System.out.print(x + " ");

  • System.out.println();

  • int[] b = new int[a.length];

  • b = a;

  • selectionSort(b);

  • System.out.println("選擇排序后:");

  • for (int x: b) System.out.print(x + " ");

  • System.out.println();

  • //測試插入排序:

  • System.out.println("插入排序前:");

  • for (int x: a) System.out.print(x + " ");

  • System.out.println();

  • int[] c = new int[a.length];

  • c = a;

  • injectionSort(c);

  • System.out.println("插入排序后:");

  • for (int x: c) System.out.print(x + " ");

  • System.out.println();

  • //測試氣泡排序:

  • System.out.println("氣泡排序前:");

  • for (int x: a) System.out.print(x + " ");

  • System.out.println();

  • int[] d = new int[a.length];

  • d = a;

  • bubbleSort(d);

  • System.out.println("氣泡排序后:");

  • for (int x: d) System.out.print(x + " ");

  • }

  • }


  • ?

    (13)快速排序(一)

    說明:

    快速排序法(quick sort)是目前所公認最快的排序方法之一(視解題的對象而定),雖然快速排序法在最差狀況下可以達O(n2),但是在多數的情況下,快速排序法的效率表現是相當不錯的。

    快速排序法的基本精神是在數列中找出適當的軸心,然后將數列一分為二,分別對左邊與右邊數列進行排序,而影響快速排序法效率的正是軸心的選擇。

    這邊所介紹的第一個快速排序法版本,是在多數的教科書上所提及的版本,因為它最容易理解,也最符合軸心分割與左右進行排序的概念,適合對初學者進行講解。

    解法:

    這邊所介紹的快速演算如下:將最左邊的數設定為軸,并記錄其值為?s

    廻圈處理:

    令索引?i 從數列左方往右方找,直到找到大于 s 的數

    令索引?j 從數列左右方往左方找,直到找到小于 s 的數

    如果?i >= j,則離開回圈

    如果?i < j,則交換索引i與j兩處的值

    將左側的軸與?j 進行交換

    對軸左邊進行遞回

    對軸右邊進行遞回

    ?

    透過以下演算法,則軸左邊的值都會小于s,軸右邊的值都會大于s,如此再對軸左右兩邊進行遞回,就可以對完成排序的目的,例如下面的實例,*表示要交換的數,[]表示軸:

    [41] 24 76* 11 45 64 21 69 19 36*

    [41] 24 36 11 45* 64 21 69 19* 76

    [41] 24 36 11 19 64* 21* 69 45 76

    [41] 24 36 11 19 21 64 69 45 76

    21 24 36 11 19 [41] 64 69 45 76

    ?

    在上面的例子中,41左邊的值都比它小,而右邊的值都比它大,如此左右再進行遞回至排序完成。

    ?

    實現:

    ?

    ?

  • //java實現

  • public class QuickSort {

  • public static void sort(int[] number) {

  • sort(number, 0, number.length - 1);

  • }

  • private static void sort(int[] number, int left, int right) {

  • if (left < right) {

  • int s = number[left];

  • int i = left;

  • int j = right + 1;

  • while (true) {

  • // 向右找

  • while (i + 1 < number.length && number[++i] < s);

  • // 向左找

  • while (j - 1 > -1 && number[--j] > s);

  • if (i >= j) break;

  • swap(number, i, j);

  • }

  • number[left] = number[j];

  • number[j] = s;

  • sort(number, left, j - 1);

  • // 對左邊進行遞回

  • sort(number, j + 1, right);

  • // 對右邊進行遞回

  • }

  • }

  • private static void swap(int[] number, int i, int j) {

  • int t;

  • t = number[i];

  • number[i] = number[j];

  • number[j] = t;

  • }

  • }

  • ?

    ?

    (14)快速排序(二)

    說明:

    在快速排序法(一)中,每次將最左邊的元素設為軸,而之前曾經說過,快速排序法的加速在于軸的選擇,在這個例子中,只將軸設定為中間的元素,依這個元素作基準進行比較,這可以增加快速排序法的效率。

    解法:

    在這個例子中,取中間的元素s作比較,同樣的先得右找比s大的索引 i,然后找比s小的索引 j,只要兩邊的索引還沒有交會,就交換 i 與 j 的元素值,這次不用再進行軸的交換了,因為在尋找交換的過程中,軸位置的元素也會參與交換的動作,例如:

    41 24 76 11 45 64 21 69 19 36

    ?

    首先left為0,right為9,(left+right)/2 = 4(取整數的商),所以軸為索引4的位置,比較的元素是45,您往右找比45大的,往左找比45小的進行交換:

    41 24 76* 11 [45] 64 21 69 19 *36

    41 24 36 11 45* 64 21 69 19* 76

    41 24 36 11 19 64* 21* 69 45 76

    [41 24 36 11 19 21] [64 69 45 76]

    ?

    完成以上之后,再初別對左邊括號與右邊括號的部份進行遞回,如此就可以完成排序的目的。

    實現:

    ?

    ?

  • public class QuickSort {

  • public static void sort(int[] number) {

  • sort(number, 0, number.length - 1);

  • }

  • private static void sort(int[] number, int left, int right) {

  • if (left < right) {

  • int s = number[(left + right) / 2];

  • int i = left - 1;

  • int j = right + 1;

  • while (true) {

  • // 向右找

  • while (number[++i] < s);

  • // 向左找

  • while (number[--j] > s);

  • if (i >= j) break;

  • swap(number, i, j);

  • }

  • sort(number, left, i - 1);

  • // 對左邊進行遞回

  • sort(number, j + 1, right);

  • // 對右邊進行遞回

  • }

  • }

  • private static void swap(int[] number, int i, int j) {

  • int t;

  • t = number[i];

  • number[i] = number[j];

  • number[j] = t;

  • }

  • }


  • ?

    ?

    ?

    (15)快速排序(三)

    說明:

    之前說過軸的選擇是快速排序法的效率關鍵之一,在這邊的快速排序法的軸選擇方式更加快了快速排序法的效率,它是來自演算法名書?Introduction to Algorithms 之中。

    解法:

    先說明這個快速排序法的概念,它以最右邊的值s作比較的標準,將整個數列分為三個部份,一個是小于s的部份,一個是大于s的部份,一個是未處理的部份,如下所示 :

    ?

    ?

    在排序的過程中,i 與 j 都會不斷的往右進行比較與交換,最后數列會變為以下的狀態:

    ?

    ?

    然后將s的值置于中間,接下來就以相同的步驟會左右兩邊的數列進行排序的動作,如下所示:

    ?

    ?

    整個演算的過程,直接摘錄書中的虛擬碼來作說明:

    ?

    實現:

  • public class QuickSort3 {

  • public static void sort(int[] number) {

  • sort(number, 0, number.length - 1);

  • }

  • private static void sort(int[] number, int left, int right) {

  • if (left < right) {

  • int q = partition(number, left, right);

  • sort(number, left, q - 1);

  • sort(number, q + 1, right);

  • }

  • }

  • private static int partition(int number[], int left, int right) {

  • int s = number[right];

  • int i = left - 1;

  • for (int j = left; j < right; j++) {

  • if (number[j] <= s) {

  • i++;

  • swap(number, i, j);

  • }

  • }

  • swap(number, i + 1, right);

  • return i + 1;

  • }

  • private static void swap(int[] number, int i, int j) {

  • int t;

  • t = number[i];

  • number[i] = number[j];

  • number[j] = t;

  • }

  • }


  • ?

    (16)合并排序

    說明:

    之前所介紹的排序法都是在同一個陣列中的排序,考慮今日有兩筆或兩筆以上的資料,它可能是不同陣列中的資料,或是不同檔案中的資料,如何為它們進行排序?

    ?

    解法:

    可以使用合并排序法,合并排序法基本是將兩筆已排序的資料合并并進行排序,如果所讀入的資料尚未排序,可以先利用其它的排序方式來處理這兩筆資料,然后再將排序好的這兩筆資料合并。

    有人問道,如果兩筆資料本身就無排序順序,何不將所有的資料讀入,再一次進行排序?排序的精神是盡量利用資料已排序的部份,來加快排序的效率,小筆資料的排序較為快速,如果小筆資料排序完成之后,再合并處理時,因為兩筆資料都有排序了,所有在合并排序時會比單純讀入所有的資料再一次排序來的有效率。

    那么可不可以直接使用合并排序法本身來處理整個排序的動作?而不動用到其它的排序方式?答案是肯定的,只要將所有的數字不斷的分為兩個等分,直到最后剩一個數字為止,然后再反過來不斷的合并,就如下圖所示:

    ?

    不過基本上分割又會花去額外的時間,不如使用其它較好的排序法來排序小筆資料,再使用合并排序來的有效率。

    ?

    實現:

  • public class MergeSort {

  • public static int[] sort(int[] number1, int[] number2) {

  • int[] number3 = new int[number1.length + number2.length];

  • int i = 0, j = 0, k = 0;

  • while (i < number1.length && j < number2.length) {

  • if (number1[i] <= number2[j]) number3[k++] = number1[i++];

  • else number3[k++] = number2[j++];

  • }

  • while (i < number1.length) number3[k++] = number1[i++];

  • while (j < number2.length) number3[k++] = number2[j++];

  • return number3;

  • }

  • }


  • ?

    (17)基數排序

    說明:

    在之前所介紹過的排序方法,都是屬于「比較性」的排序法,也就是每次排序時?,都是比較整個鍵值的大小以進行排序。

    這邊所要介紹的「基數排序法」(radix sort)則是屬于「分配式排序」(distribution sort),基數排序法又稱「桶子法」(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些「桶」中,藉以達到排序的作用,基數排序法是屬于穩定性的排序,其時間復雜度為O (nlog(r)m),其中r為所采取的基數,而m為堆數,在某些時候,基數排序法的效率高于其它的比較性排序法。

    ?

    解法:

    基數排序的方式可以采用LSD(Least sgnificant digital)或MSD(Most sgnificant digital),LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。

    以LSD為例,假設原來有一串數值如下所示:

    73, 22, 93, 43, 55, 14, 28, 65, 39, 81

    首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    ?

    ?

    81

    ?

    ?

    ?

    ?

    ?

    ?

    65

    ?

    ?

    ?

    ?

    ?

    ?

    39

    ?

    ?

    ?

    ?

    ?

    ?

    43

    14

    55

    ?

    ?

    ?

    ?

    28

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    93

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    22

    73

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    接下來將這些桶子中的數值重新串接起來,成為以下的數列:

    81, 22, 73, 93, 43, 14, 55, 65, 28, 39

    接著再進行一次分配,這次是根據十位數來分配:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    ?

    ?

    28

    39

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    14

    22

    ?

    ?

    43

    55

    65

    73

    81

    93

    ?

    接下來將這些桶子中的數值重新串接起來,成為以下的數列:

    14, 22, 28, 39, 43, 55, 65, 73, 81, 93

    這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止。

    LSD的基數排序適用于位數小的數列,如果位數多的話,使用MSD的效率會比較好,MSD的方式恰與LSD相反,是由高位數為基底開始進行分配,其他的演 算方式則都相同。

    ?

    實現:

    ?

    ?

  • public class RadixSort {

  • public static void sort(int[] number, int d) {

  • int k = 0;

  • int n = 1;

  • int[][] temp = new int[number.length][number.length];

  • int[] order = new int[number.length];

  • while (n <= d) {

  • for (int i = 0; i < number.length; i++) {

  • int lsd = ((number[i] / n) % 10);

  • temp[lsd][order[lsd]] = number[i];

  • order[lsd]++;

  • }

  • for (int i = 0; i < number.length; i++) {

  • if (order[i] != 0)

  • for (int j = 0; j < order[i]; j++) {

  • number[k] = temp[i][j];

  • k++;

  • }

  • order[i] = 0;

  • }

  • n *= 10;

  • k = 0;

  • }

  • }

  • public static void main(String[] args) {

  • int[] data = {

  • 73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100

  • };

  • RadixSort.sort(data, 100);

  • for (int i = 0; i < data.length; i++) {

  • System.out.print(data[i] + " ");

  • }

  • }

  • }

  • ?

    ?

    (18)循序查找法(使用衛兵)

    說明:

    搜尋的目的,是在「已排序的資料」中尋找指定的資料,而當中循序搜尋是最基本的搜尋法,只要從資料開頭尋找到最后,看看是否找到資料即可。

    ?

    解法:

    ????初學者看到循序搜尋,多數都會使用以下的方式來進行搜尋:

    while(i < MAX) {

    ????if(number[i] == k) {

    ????????printf("找到指定值");

    ????????break;

    ????}

    ????i++;

    }

    這個方法基本上沒有錯,但是可以加以改善,可以利用設定衛兵的方式,省去if判斷式,衛兵通常設定在數列最后或是最前方,假設設定在列前方好了(索引0的 位置),我們從數列后方向前找,如果找到指定的資料時,其索引值不是0,表示在數列走訪完之前就找到了,在程式的撰寫上,只要使用一個while回圈就可 以了。

    實現:

  • public class LinearSearch {

  • public static int search(int[] number, int des) {

  • int[] tmp = new int[number.length + 1];

  • for (int i = 1; i < tmp.length; i++) {

  • tmp[i] = number[i - 1];

  • }

  • tmp[0] = des;

  • int k = tmp[0];

  • int i = number.length;

  • while (tmp[i] != k) i--;

  • return i - 1;

  • }

  • public static void main(String[] args) {

  • int[] number = {

  • 1, 4, 2, 6, 7, 3, 9, 8

  • };

  • QuickSort.sort(number);

  • int find = LinearSearch.search(number, 3);

  • if (find != 0) System.out.println("找到數值于索引" + find);

  • else System.out.println("找不到數值");

  • }

  • }


  • ?

    (19)二分查找法

    說明:

    如果搜尋的數列已經有排序,應該盡量利用它們已排序的特性,以減少搜尋比對的次數,這是搜尋的基本原則,二分搜尋法是這個基本原則的代表。

    解法:

    ????在二分搜尋法中,從數列的中間開始搜尋,如果這個數小于我們所搜尋的數,由于數列已排序,則該數左邊的數一定都小于要搜尋的對象,所以無需浪費時間在左邊的數;如果搜尋的數大于所搜尋的對象,則右邊的數無需再搜尋,直接搜尋左邊的數。

    所以在二分搜尋法中,將數列不斷的分為兩個部份,每次從分割的部份中取中間數比對,例如要搜尋92于以下的數列,首先中間數索引為(0+9)/2 = 4(索引由0開始):

    [3 24 57 57?67?68 83 90 92 95]

    由于67小于92,所以轉搜尋右邊的數列:

    3 24 57 57 67 [68 83?90?92 95]

    由于90小于92,再搜尋右邊的數列,這次就找到所要的數了:

    3 24 57 57 67 68 83 90 [92?95]

    ?

    實現:

  • public class BinarySearch {

  • public static int search(int[] number, int des) {

  • int low = 0;

  • int upper = number.length - 1;

  • while (low <= upper) {

  • int mid = (low + upper) / 2;

  • if (number[mid] < des) low = mid + 1;

  • else if (number[mid] > des) upper = mid - 1;

  • else return mid;

  • }

  • return -1;

  • }

  • public static void main(String[] args) {

  • int[] number = {

  • 1, 4, 2, 6, 7, 3, 9, 8

  • };

  • QuickSort.sort(number);

  • int find = BinarySearch.search(number, 3);

  • if (find != -1) System.out.println("找到數值于索引" + find);

  • else System.out.println("找不到數值");

  • }

  • }


  • ?

    (20)插補查找法

    說明:

    如果卻搜尋的資料分布平均的話,可以使用插補(Interpolation)搜尋法來進行搜尋,在搜尋的對象大于500時,插補搜尋法會比 二分搜尋法 來的快速。

    ?

    解法:

    插補搜尋法是以資料分布的近似直線來作比例運算,以求出中間的索引并進行資料比對,如果取出的值小于要尋找的值,則提高下界,如果取出的值大于要尋找的值,則降低下界,如此不斷的減少搜尋的范圍,所以其本原則與二分搜尋法是相同的,至于中間值的尋找是透過比例運算,如下所示,其中K是指定要尋找的對象, 而m則是可能的索引值:

    ?

    ?

    實現:

  • public class InterpolationSearch {

  • public static int search(int[] number, int des) {

  • int low = 0;

  • int upper = number.length - 1;

  • while (low <= upper) {

  • int mid = (upper - low) * (des - number[low]) / (number[upper] - number[low]) + low;

  • if (mid < low || mid > upper) return -1;

  • if (des < number[mid]) upper = mid - 1;

  • else if (des > number[mid]) low = mid + 1;

  • else return mid;

  • }

  • return -1;

  • }

  • public static void main(String[] args) {

  • int[] number = {

  • 1, 4, 2, 6, 7, 3, 9, 8

  • };

  • QuickSort.sort(number);

  • int find = InterpolationSearch.search(number, 3);

  • if (find != -1) System.out.println("找到數值于索引" + find);

  • else System.out.println("找不到數值");

  • }

  • }


  • ?

    ?

    (21)費式查找法

    說明:

    二分搜尋法每次搜尋時,都會將搜尋區間分為一半,所以其搜尋時間為O(log(2)n),log(2)表示以2為底的log值,這邊要介紹的費氏搜尋,其利用費氏數列作為間隔來搜尋下一個數,所以區間收斂的速度更快,搜尋時間為O(logn)。

    ?

    解法:

    ????費氏搜尋使用費氏數列來決定下一個數的搜尋位置,所以必須先制作費氏數列,這在之前有提過;費氏搜尋會先透過公式計算求出第一個要搜尋數的位置,以及其代表的費氏數,以搜尋對象10個數字來說,第一個費氏數經計算后一定是F5,而第一個要搜尋的位置有兩個可能,例如若在下面的數列搜尋的話(為了計算方便, 通常會將索引0訂作無限小的數,而數列由索引1開始):

    ?

    -infin; 1 3 5 7 9 13 15 17 19 20

    ?

    如果要搜尋5的話,則由索引F5 = 5開始搜尋,接下來如果數列中的數小于指定搜尋值時,就往左找,大于時就向右,每次找的間隔是F4、F3、F2來尋找,當費氏數為0時還沒找到,就表示尋找失敗,如下所示:

    ?

    ?

    由于第一個搜尋值索引F5 = 5處的值小于19,所以此時必須對齊數列右方,也就是將第一個搜尋值的索引改為F5+2 = 7,然后如同上述的方式進行搜尋,如下所示:

    ?

    至于第一個搜尋值是如何找到的?我們可以由以下這個公式來求得,其中n為搜尋對象的個數:

    Fx + m = n

    Fx <= n

    也就是說Fx必須找到不大于n的費氏數,以10個搜尋對象來說:

    Fx + m = 10

    ????取Fx?= 8, m = 2,所以我們可以對照費氏數列得x = 6,然而第一個數的可能位置之一并不是F6,而是第x-1的費氏數,也就是F5?= 5。

    如果數列number在索引5處的值小于指定的搜尋值,則第一個搜尋位置就是索引5的位置,如果大于指定的搜尋值,則第一個搜尋位置必須加上m,也就是F5?+ m = 5 + 2 = 7,也就是索引7的位置,其實加上m的原因,是為了要讓下一個搜尋值剛好是數列的最后一個位置。

    費氏搜尋看來難懂,但只要掌握Fx?+ m = n這個公式,自己找幾個實例算一次,很容易就可以理解;費氏搜尋除了收斂快速之外,由于其本身只會使用到加法與減法,在運算上也可以加快。

    ?

    實現:

    ?

  • public class FibonacciSearch {

  • public static int search(int[] number, int des) {

  • int[] fib = createFibonacci(number.length);

  • int x = findX(fib, number.length + 1, des);

  • int m = number.length - fib[x];

  • x--;

  • int i = x;

  • if (number[i] < des) i += m;

  • while (fib[x] > 0) {

  • if (number[i] < des) i += fib[--x];

  • else if (number[i] > des) i -= fib[--x];

  • else return i;

  • }

  • return -1;

  • }

  • private static int[] createFibonacci(int max) {

  • int[] fib = new int[max];

  • for (int i = 0; i < fib.length; i++) {

  • fib[i] = Integer.MIN_VALUE;

  • }

  • fib[0] = 0;

  • fib[1] = 1;

  • for (int i = 2; i < max; i++) fib[i] = fib[i - 1] + fib[i - 2];

  • return fib;

  • }

  • private static int findX(int[] fib, int n, int des) {

  • int i = 0;

  • while (fib[i] <= n) i++;

  • i--;

  • return i;

  • }

  • public static void main(String[] args) {

  • int[] number = {

  • 1, 4, 2, 6, 7, 3, 9, 8

  • };

  • QuickSort.sort(number);

  • int find = FibonacciSearch.search(number, 3);

  • if (find != -1) System.out.println("找到數值于索引" + find);

  • else System.out.println("找不到數值");

  • }

  • }


  • ?

    ?

    ?

    (22)稀疏矩陣

    說明:

    ????如果在矩陣中,多數的元素并沒有資料,稱此矩陣為稀疏矩陣(sparse matrix),由于矩陣在程式中常使用二維陣列表示,二維陣列的大小與使用的記憶體空間成正比,如果多數的元素沒有資料,則會造成記憶體空間的浪費,為 此,必須設計稀疏矩陣的陣列儲存方式,利用較少的記憶體空間儲存完整的矩陣資訊。

    ?

    解法:

    在這邊所介紹的方法較為簡單,陣列只儲存矩陣的行數、列數與有資料的索引位置及其值,在需要使用矩陣資料時,再透過程式運算加以還原,例如若矩陣資料如下,其中0表示矩陣中該位置沒有資料:

    0 0 0 0 0 0

    0 3 0 0 0 0

    0 0 0 6 0 0

    0 0 9 0 0 0

    0 0 0 0 12 0

    這個矩陣是5X6矩陣,非零元素有4個,您要使用的陣列第一列記錄其列數、行數與非零元素個數:

    5 6 4

    陣列的第二列起,記錄其位置的列索引、行索引與儲存值:

    1 1 3

    2 3 6

    3 2 9

    4 4 12

    所以原本要用30個元素儲存的矩陣資訊,現在只使用了15個元素來儲存,節省了不少記憶體的使用。

    ?

    實現:

    ?

  • public class SparseMatrix {

  • public static int[][] restore(int[][] sparse) {

  • int row = sparse[0][0];

  • int column = sparse[0][1];

  • int[][] array = new int[row][column];

  • int k = 1;

  • for (int i = 0; i < row; i++) {

  • for (int j = 0; j < column; j++) {

  • if (k <= sparse[0][2] && i == sparse[k][0] && j == sparse[k][1]) {

  • array[i][j] = sparse[k][2];

  • k++;

  • } else array[i][j] = 0;

  • }

  • }

  • return array;

  • }

  • public static void main(String[] args) {

  • int[][] sparse = {

  • {

  • 5, 6, 4

  • }, {

  • 1, 1, 3

  • }, {

  • 2, 3, 6

  • }, {

  • 3, 2, 9

  • }, {

  • 4, 4, 12

  • }

  • };

  • int[][] array = SparseMatrix.restore(sparse);

  • for (int i = 0; i < array.length; i++) {

  • for (int j = 0; j < array[i].length; j++) {

  • System.out.print(array[i][j] + " ");

  • }

  • System.out.println();

  • }

  • }

  • }


  • ?

    ?

    ?

    (23)多維矩陣轉一維矩陣

    說明:

    ????有的時候,為了運算方便或資料儲存的空間問題,使用一維陣列會比二維或多維陣列來得方便,例如上三角矩陣、下三角矩陣或對角矩陣,使用一維陣列會比使用二維陣列來得節省空間。

    解法:

    以二維陣列轉一維陣列為例,索引值由0開始,在由二維陣列轉一維陣列時,我們有兩種方式:「以列(Row)為主」或「以行(Column)為主」。由于 C/C++、Java等的記憶體配置方式都是以列為主,所以您可能會比較熟悉前者(Fortran的記憶體配置方式是以行為主)。

    以列為主的二維陣列要轉為一維陣列時,是將二維陣列由上往下一列一列讀入一維陣列,此時索引的對應公式如下所示,其中row與column是二維陣列索引,loc表示對應的一維陣列索引:

    loc = column + row*行數

    以行為主的二維陣列要轉為一維陣列時,是將二維陣列由左往右一行一行讀入一維陣列,此時索引的對應公式如下所示:

    loc = row + column*列數

    公式的推導您畫圖看看就知道了,如果是三維陣列,則公式如下所示,其中i(個數u1)、j(個數u2)、k(個數u3)分別表示三維陣列的三個索引:

    以列為主:loc = i*u2*u3 + j*u3 + k

    以行為主:loc = k*u1*u2 + j*u1 + i

    ????更高維度的可以自行依此類推,但通常更高維度的建議使用其它資料結構(例如物件包裝)會比較具體,也不易搞錯。

    ?

    實現:

    ??

  • public class TwoDimArray {

  • public static int[] toOneDimByRow(int[][] array) {

  • int[] arr = new int[array.length * array[0].length];

  • for (int row = 0; row < array.length; row++) {

  • for (int column = 0; column < array[0].length; column++) {

  • int i = column + row * array[0].length;

  • arr[i] = array[row][column];

  • }

  • }

  • return arr;

  • }

  • public static int[] toOneDimByColumn(int[][] array) {

  • int[] arr = new int[array.length * array[0].length];

  • for (int row = 0; row < array.length; row++) {

  • for (int column = 0; column < array[0].length; column++) {

  • int i = i = row + column * array.length;

  • arr[i] = array[row][column];

  • }

  • }

  • return arr;

  • }

  • }


  • ?

    ?

    (24)上三角、下三角、對稱矩陣

    說明:

    ????上三角矩陣是矩陣在對角線以下的元素均為0,即Aij?= 0,i > j,例如:

    1 ?2 ?3 ??4 ??5

    0 ?6 ?7 ??8 ??9

    0 ?0 ?10 ??11 ?12

    0 ?0 ?0 ??13 ?14

    0 ?0 ?0 ??0 ?15

    下三角矩陣是矩陣在對角線以上的元素均為0,即Aij?= 0,i < j,例如:

    ?1 ?0 ?0 ?0 ?0

    ?2 ?6 ?0 ?0 ?0

    ?3 ?7 ?10 0 ?0

    ?4 ?8 ?11 13 0

    ?5 ?9 ?12 14 15

    對稱矩陣是矩陣元素對稱于對角線,例如:

    ?1 ?2 ?3 ?4 ?5

    ?2 ?6 ?7 ?8 ?9

    ?3 ?7 ?10 11 12

    ?4 ?8 ?11 13 14

    ?5 ?9 ?12 14 15

    上三角或下三角矩陣也有大部份的元素不儲存值(為0),我們可以將它們使用一維陣列來儲存以節省儲存空間,而對稱矩陣因為對稱于對角線,所以可以視為上三角或下三角矩陣來儲存。

    解法:

    假設矩陣為nxn,為了計算方便,我們讓陣列索引由1開始,上三角矩陣化為一維陣列,若以列為主,其公式為:loc = n*(i-1) - i*(i-1)/2 + j

    化為以行為主,其公式為:loc = j*(j-1)/2 + i

    下三角矩陣化為一維陣列,若以列為主,其公式為:loc = i*(i-1)/2 + j

    若以行為主,其公式為:loc = n*(j-1) - j*(j-1)/2 + i

    實現:

    ?

  • public class TriangleArray {

  • private int[] arr;

  • private int length;

  • public TriangleArray(int[][] array) {

  • length = array.length;

  • arr = new int[length * (1 + length) / 2];

  • int loc = 0;

  • for (int i = 0; i < length; i++) {

  • for (int j = 0; j < length; j++) {

  • if (array[i][j] != 0) arr[loc++] = array[i][j];

  • }

  • }

  • }

  • public int getValue(int i, int j) {

  • int loc = length * i - i * (i + 1) / 2 + j;

  • return arr[loc];

  • }

  • public static void main(String[] args) {

  • int[][] array = {

  • {

  • 1, 2, 3, 4, 5

  • }, {

  • 0, 6, 7, 8, 9

  • }, {

  • 0, 0, 10, 11, 12

  • }, {

  • 0, 0, 0, 13, 14

  • }, {

  • 0, 0, 0, 0, 15

  • }

  • };

  • TriangleArray triangleArray = new TriangleArray(array);

  • System.out.print(triangleArray.getValue(2, 2));

  • }

  • }


  • ?

    ?

    (25)奇數魔方陣

    說明:

    ????將1到n(為奇數)的數字排列在nxn的方陣上,且各行、各列與各對角線的和必須相同,如下所示:

    ?

    ?

    解法:

    ????填魔術方陣的方法以奇數最為簡單,第一個數字放在第一行第一列的正中央,然后向右(左)上填,如果右(左)上已有數字,則向下填,如下圖所示:

    ?

    一般程式語言的陣列索引多由0開始,為了計算方便,我們利用索引1到n的部份,而在計算是向右(左)上或向下時,我們可以將索引值除以n值,如果得到余數為1就向下,否則就往右(左)上,原理很簡單,看看是不是已經在同一列上繞一圈就對了。

    ?

    實現:

    ??

  • public class Matrix {

  • public static int[][] magicOdd(int n) {

  • int[][] square = new int[n + 1][n + 1];

  • int i = 0;

  • int j = (n + 1) / 2;

  • for (int key = 1; key <= n * n; key++) {

  • if ((key % n) == 1) i++;

  • else {

  • i--;

  • j++;

  • }

  • if (i == 0) i = n;

  • if (j > n) j = 1;

  • square[i][j] = key;

  • }

  • int[][] matrix = new int[n][n];

  • for (int k = 0; k < matrix.length; k++) {

  • for (int l = 0; l < matrix[0].length; l++) {

  • matrix[k][l] = square[k + 1][l + 1];

  • }

  • }

  • return matrix;

  • }

  • public static void main(String[] args) {

  • int[][] magic = Matrix.magicOdd(5);

  • for (int k = 0; k < magic.length; k++) {

  • for (int l = 0; l < magic[0].length; l++) {

  • System.out.print(magic[k][l] + " ");

  • }

  • System.out.println();

  • }

  • }

  • }


  • ?

    ?

    (26)4N魔方陣

    說明:

    ????與?奇數魔術方陣?相同,在于求各行、各列與各對角線的和相等,而這次方陣的維度是4的倍數。

    解法:

    ????先來看看4X4方陣的解法:

    ?

    簡單的說,就是一個從左上由1依序開始填,但遇對角線不填,另一個由左上由16開始填,但只填在對角線,再將兩個合起來就是解答了;如果N大于2,則以 4X4為單位畫對角線:

    ?

    至于對角線的位置該如何判斷,有兩個公式,有興趣的可以畫圖印證看看,如下所示:

    左上至右下:j % 4 == i % 4

    右上至左下:(j % 4 + i % 4) == 1

    實現:

    ?

  • public class Matrix2 {

  • public static int[][] magicFourN(int n) {

  • int[][] square = new int[n + 1][n + 1];

  • for (int j = 1; j <= n; j++) {

  • for (int i = 1; i <= n; i++) {

  • if (j % 4 == i % 4 || (j % 4 + i % 4) == 1) square[i][j] = (n + 1 - i) * n - j + 1;

  • else square[i][j] = (i - 1) * n + j;

  • }

  • }

  • int[][] matrix = new int[n][n];

  • for (int k = 0; k < matrix.length; k++) {

  • for (int l = 0; l < matrix[0].length; l++) {

  • matrix[k][l] = square[k + 1][l + 1];

  • }

  • }

  • return matrix;

  • }

  • public static void main(String[] args) {

  • int[][] magic = Matrix2.magicFourN(8);

  • for (int k = 0; k < magic.length; k++) {

  • for (int l = 0; l < magic[0].length; l++) {

  • System.out.print(magic[k][l] + " ");

  • }

  • System.out.println();

  • }

  • }

  • }


  • ?

    ?

    ?

    (27)2(2n+1)魔方陣

    說明:

    ????方陣的維度整體來看是偶數,但是其實是一個奇數乘以一個偶數,例如6X6,其中6=2X3,我們也稱這種方陣與單偶數方陣。

    解法:

    ????如果您會解奇數魔術方陣,要解這種方陣也就不難理解,首先我們令n=2(2m+1),并將整個方陣看作是數個奇數方陣的組合,如下所示:

    ?

    首先依序將A、B、C、D四個位置,依奇數方陣的規則填入數字,填完之后,方陣中各行的和就相同了,但列與對角線則否,此時必須在A-D與C- B之間,作一些對應的調換,規則如下:

    將A中每一列(中間列除外)的頭m個元素,與D中對應位置的元素調換。

    將A的中央列、中央那一格向左取m格,并與D中對應位置對調

    將C中每一列的倒數m-1個元素,與B中對應的元素對調

    舉個實例來說,如何填6X6方陣,我們首先將之分解為奇數方陣,并填入數字,如下所示:

    ?

    接下來進行互換的動作,互換的元素以不同顏色標示,如下:

    ?

    ?

    實現:

    ???

    ?

  • public class Matrix3 {

  • public static int[][] magic22mp1(int n) {

  • int[][] square = new int[n][n];

  • magic_o(square, n / 2);

  • exchange(square, n);

  • return square;

  • }

  • private static void magic_o(int[][] square, int n) {

  • int row = 0;

  • int column = n / 2;

  • for (int count = 1; count <= n * n; count++) {

  • square[row][column] = count;

  • // 填A

  • square[row + n][column + n] = count + n * n;

  • // 填B

  • square[row][column + n] = count + 2 * n * n;

  • // 填C

  • square[row + n][column] = count + 3 * n * n;

  • // 填D

  • if (count % n == 0) row++;

  • else {

  • row = (row == 0) ? n - 1 : row - 1;

  • column = (column == n - 1) ? 0 : column + 1;

  • }

  • }

  • }

  • private static void exchange(int[][] x, int n) {

  • int i, j;

  • int m = n / 4;

  • int m1 = m - 1;

  • for (i = 0; i < n / 2; i++) {

  • if (i != m) {

  • for (j = 0; j < m; j++)

  • // 處理規則 1

  • swap(x, i, j, n / 2 + i, j);

  • for (j = 0; j < m1; j++)

  • // 處理規則 2

  • swap(x, i, n - 1 - j, n / 2 + i, n - 1 - j);

  • } else {

  • // 處理規則 3

  • for (j = 1; j <= m; j++) swap(x, m, j, n / 2 + m, j);

  • for (j = 0; j < m1; j++) swap(x, m, n - 1 - j, n / 2 + m, n - 1 - j);

  • }

  • }

  • }

  • private static void swap(int[][] number, int i, int j, int k, int l) {

  • int t;

  • t = number[i][j];

  • number[i][j] = number[k][l];

  • number[k][l] = t;

  • }

  • public static void main(String[] args) {

  • int[][] magic = Matrix3.magic22mp1(6);

  • for (int k = 0; k < magic.length; k++) {

  • for (int l = 0; l < magic[0].length; l++) {

  • System.out.print(magic[k][l] + " ");

  • }

  • System.out.println();

  • }

  • }

  • ?
  • 總結

    以上是生活随笔為你收集整理的java一些必会算法(转自落尘曦的博客:http://blog.csdn.net/qq_23994787。 )的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    日韩免费在线观看 | 五月天九九 | 五月婷婷精品 | 国产在线看一区 | 伊人久久在线观看 | 久久午夜国产 | 四川妇女搡bbbb搡bbbb搡 | 午夜婷婷在线播放 | 国产免费黄色 | 国产精品视频地址 | 99久久99| 中中文字幕av在线 | 午夜精品一区二区三区四区 | 亚洲性视频 | 99在线精品视频 | 天天天色| 日韩精品一区二区三区水蜜桃 | 国产精品网红福利 | 国内丰满少妇猛烈精品播 | 欧美日韩精品在线播放 | 精品成人国产 | 在线视频免费观看 | 国内精品国产三级国产aⅴ久 | 国产精品短视频 | 欧美精彩视频在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产高清区 | 国产精品乱码久久 | 亚洲精品国产片 | 99re国产视频 | 国产精品一区二区在线观看免费 | 国产精品丝袜久久久久久久不卡 | 免费在线观看av网站 | 瑞典xxxx性hd极品 | 日韩一区在线播放 | 66av99精品福利视频在线 | 在线小视频 | 五月婷婷视频 | 亚洲专区在线视频 | caobi视频| 中文字幕av在线播放 | 伊人天天操 | 欧美日韩视频网站 | 日韩精品你懂的 | 久久午夜精品 | 午夜av剧场 | jizzjizzjizz亚洲 | 玖玖视频免费在线 | 一级免费观看 | 丁香六月伊人 | 日韩专区av| 日韩视频免费观看高清完整版在线 | 九九免费观看全部免费视频 | 久久99国产精品免费 | 久久99欧美 | 精品国产理论片 | 国产精品久久麻豆 | 久久99精品久久只有精品 | 亚洲国产精品日韩 | 日本精品久久 | 二区三区av | 久久精品一二三区 | 精品亚洲va在线va天堂资源站 | 欧美精品在线观看免费 | 天天摸天天舔天天操 | 午夜视频免费在线观看 | 四川bbb搡bbb爽爽视频 | 久久99精品久久久久久三级 | 最新日韩精品 | 99热99| 在线视频中文字幕一区 | 黄色一二级片 | 国产一区二区在线视频观看 | 国产片免费在线观看视频 | 伊人丁香 | 欧美日韩免费网站 | 欧美激情视频在线免费观看 | 欧美日韩国产二区三区 | 在线观看免费观看在线91 | 日本中文乱码卡一卡二新区 | 日韩精品中文字幕在线 | 欧美一级久久久 | 青青草在久久免费久久免费 | 婷婷干五月| 久久精品香蕉视频 | 日日天天| 国语精品免费视频 | 亚洲欧美va | 五月婷在线 | 91久久国产综合精品女同国语 | 国产精品久久久久久久久久久杏吧 | 五月婷综合 | www.天天干.com| 精品婷婷 | 亚州视频在线 | 国产精品一区二区三区免费看 | 国产精品初高中精品久久 | av经典在线| 久久国色夜色精品国产 | 91网在线看 | 亚洲夜夜爽| free,性欧美| 免费看片成人 | 日本在线观看中文字幕无线观看 | av在线一 | 日韩网站在线 | 久久99免费| 激情综合电影网 | 国产91丝袜在线播放动漫 | 国语精品免费视频 | 欧美va天堂va视频va在线 | 欧美aaa级片 | 欧美大片大全 | 日韩激情一二三区 | 久久精品麻豆 | 国产成人精品一区二区在线 | 午夜精品久久一牛影视 | 青青网视频 | 五月天婷亚洲天综合网精品偷 | 日本黄色免费观看 | 国产视频91在线 | 日韩在线视频观看免费 | 日韩av一区二区在线影视 | 日韩欧美在线观看一区 | 日韩精品免费一线在线观看 | av在线免费网站 | 亚洲精品视频在线播放 | 91精品国产乱码久久 | 开心激情五月网 | 亚洲va韩国va欧美va精四季 | 成人h在线播放 | 中文字幕亚洲精品在线观看 | 四虎在线免费视频 | 又长又大又黑又粗欧美 | 国产一区二区在线免费播放 | 欧美日韩精品网站 | 日韩簧片在线观看 | 人人插人人草 | 国产精品国产精品 | 欧美巨乳波霸 | 午夜精品久久久久久久爽 | 日韩在线观看一区二区三区 | 激情自拍av | 中文字幕在线视频一区二区 | 91tv国产成人福利 | 久久韩国免费视频 | 国产免费一区二区三区最新 | 国产精品欧美日韩 | 少妇自拍av | 五月婷激情 | 午夜性生活 | 精品国产乱码久久久久久1区2匹 | 成人午夜电影网 | 日韩有码中文字幕在线 | 天堂av在线中文在线 | 天天色天天操综合 | 91看片在线观看 | 国内精品久久久久久久久 | 国产精品久久久久av福利动漫 | 国产一区在线观看免费 | 国产精品毛片久久久久久久 | 国产成人综合在线观看 | 成人在线观看免费 | 日韩激情一二三区 | 亚洲精品自拍视频在线观看 | 香蕉在线影院 | 欧洲一区二区三区精品 | 黄色三级网站 | 日韩 在线| 欧美 日韩 国产 中文字幕 | 久久国产精品电影 | 91亚洲精品久久久蜜桃借种 | 开心色婷婷 | 久久精品亚洲精品国产欧美 | 免费高清在线观看成人 | 91探花视频| 精品中文字幕在线播放 | 国产伦精品一区二区三区四区视频 | 国产黄视频在线观看 | 亚洲精品18日本一区app | 国产精品福利无圣光在线一区 | 色婷婷激情 | 亚洲美女在线一区 | 国产免费观看高清完整版 | 激情欧美在线观看 | 特黄特色特刺激视频免费播放 | 天堂久久电影网 | 国产在线播放不卡 | 99精彩视频在线观看免费 | 日韩精品一区二区三区免费视频观看 | 久久精品视频国产 | 欧美一级视频在线观看 | 免费在线观看av片 | 99精品在线播放 | 欧美精品在线一区 | 成人黄在线 | 日韩在线观看三区 | 亚洲第一区在线观看 | 精品国产视频在线观看 | 日韩精品短视频 | 永久免费精品视频网站 | 国产区久久 | 午夜精品久久久久久久99婷婷 | 亚洲一区不卡视频 | www.天天综合 | 亚洲少妇激情 | www最近高清中文国语在线观看 | av先锋影音少妇 | 一级黄色片在线观看 | 国产经典三级 | 国产黄色片网站 | 欧美精品久久 | 日韩高清黄色 | 免费看搞黄视频网站 | 中国一级特黄毛片大片久久 | 久久一及片 | 日韩中文字幕在线观看 | 91你懂的 | 国产一区二区高清视频 | 蜜臀aⅴ国产精品久久久国产 | 中文av影院 | www.神马久久 | a午夜电影 | 婷婷精品国产一区二区三区日韩 | 日本天天操 | 亚洲视频一区二区三区在线观看 | 激情网色 | 日韩av二区| 97在线免费观看 | 日本中文字幕电影在线免费观看 | 久草视频在线免费播放 | av电影中文字幕在线观看 | 亚洲精品乱码久久久久久高潮 | 国产91精品久久久久 | 久久久久久国产精品 | 开心婷婷色 | 国产拍在线| 色综合中文字幕 | 国产在线观看你懂得 | 在线久草视频 | 亚洲欧洲久久久 | 丁香激情五月婷婷 | 91大神精品视频在线观看 | 超碰999 | 国产午夜麻豆影院在线观看 | 一级久久久 | 日日操日日操 | 免费网站污 | 中文字幕在线看人 | 亚洲视频久久久 | 中文字幕免费播放 | 在线成人小视频 | 精品99999| 成人黄色中文字幕 | 国产在线综合视频 | 日韩色综合网 | 免费人成在线观看 | www.99热精品 | 国产精品一区二区美女视频免费看 | 成年人免费在线 | 欧美va天堂va视频va在线 | 国产精品久久久久永久免费观看 | 91麻豆精品一区二区三区 | 在线观看一级 | 友田真希x88av| 97免费公开视频 | 天天干天天干天天干 | 97国产一区 | 胖bbbb搡bbbb擦bbbb| 欧美在线久久 | 日韩三级视频在线观看 | 国产精品久久在线 | 国产不卡免费 | 欧美一级专区免费大片 | 伊人久久婷婷 | 久久久午夜精品理论片中文字幕 | 99精品国产一区二区三区不卡 | 在线观看国产麻豆 | 成人观看视频 | 国产高清无线码2021 | 日本黄色免费观看 | 激情五月播播久久久精品 | 91视频3p| 精品一区二区三区四区在线 | 国产午夜激情视频 | 激情久久影院 | 精品国自产在线观看 | 91麻豆高清视频 | 网站在线观看你们懂的 | 欧美日韩一区二区在线观看 | 久久久国际精品 | 永久免费观看视频 | 这里有精品在线视频 | 国产一区欧美日韩 | 日日夜夜天天久久 | 国产原创91| 欧美孕妇视频 | 久久国产亚洲视频 | 成人观看| 一区二区三区观看 | 成人小视频在线 | 日韩va在线观看 | 欧美91片| 日韩av网站在线播放 | 国产精品一区二区在线观看免费 | 亚洲精品乱码久久久一二三 | 亚洲精品国产第一综合99久久 | 国产精品亚洲片夜色在线 | 欧美日韩高清一区二区三区 | 综合伊人久久 | 1024手机在线看 | 高清国产午夜精品久久久久久 | 中文字幕在线免费播放 | 亚洲午夜久久久久久久久电影网 | av免费观看网址 | 日韩欧美精品一区二区 | 国产成人精品福利 | 91在线中文| 天天av在线播放 | 久久久综合九色合综国产精品 | 人人爽人人做 | 色狠狠综合天天综合综合 | 一区二区三区在线看 | 亚洲 欧美日韩 国产 中文 | 99视频免费 | 日韩av电影中文字幕在线观看 | 性色av一区二区 | av高清网站在线观看 | 国产精品国产三级国产aⅴ9色 | av黄在线播放 | 99精品免费久久久久久久久 | www久久精品 | 9在线观看免费高清完整版 玖玖爱免费视频 | 天天干夜夜夜 | www.激情五月.com | 三级在线视频观看 | 久草免费在线观看 | 国产精品igao视频网网址 | 国语精品免费视频 | 午夜黄色一级片 | 99久久精品视频免费 | 91精品免费看 | 在线最新av| 午夜性生活片 | 四虎在线永久免费观看 | 国产精品视频一二三 | 免费观看日韩av | 99精品免费网 | 精品国产一二区 | 日韩最新在线视频 | 激情婷婷久久 | 久久精品美女 | 久久久精品一区二区 | 日韩欧美69| 在线观看中文字幕网站 | 日韩女同av | 天天要夜夜操 | 国产视频在线观看一区二区 | 日韩在线观看a | 99热这里只有精品免费 | 亚洲精品视频第一页 | 国产精品自产拍在线观看 | 日韩av免费大片 | 欧美国产日韩久久 | 91精品久久久久久久99蜜桃 | 中文一二区 | 日韩av视屏在线观看 | 成人超碰97 | 久精品视频在线观看 | 97麻豆视频| 久久国产精品色婷婷 | 欧美日韩国产页 | 91亚洲在线观看 | 久久99精品国产麻豆宅宅 | 99亚洲国产| 伊人中文字幕在线 | 国产成人精品一区二区三区福利 | 极品久久久久 | 免费在线国产视频 | 欧美性色综合网 | 黄色在线免费观看网站 | 国产亚洲人成网站在线观看 | 91香蕉视频在线下载 | 久久国产精品免费视频 | 在线免费观看黄色 | 国产精品第10页 | 亚洲国产免费网站 | 欧美精品一区二区性色 | 人人射人人爽 | 中文字幕日韩免费视频 | 国产精品久久久久毛片大屁完整版 | 97在线看| 精品视频专区 | 国产成人三级在线 | 亚洲成人av影片 | 国产精品成人品 | 97成人资源站 | 欧美黑吊大战白妞欧美 | 黄污视频网站大全 | 国产高清在线一区 | 在线欧美中文字幕 | 综合在线色 | 99久久精品免费一区 | 国产在线中文字幕 | 视频一区在线免费观看 | 国产一区二区在线观看免费 | 久久精品91视频 | 午夜精品一区二区三区在线观看 | 最近中文字幕免费av | 99视频播放| 一区二区三区国产精品 | 欧美精品一区在线 | 中文字幕在线播放日韩 | 999久久精品 | 久久国产精品电影 | 一区二区视频在线免费观看 | 婷婷丁香激情 | 激情中文字幕 | 人人爽人人爽人人片av免 | 国产精品一区二区久久精品爱涩 | 美女久久久久久久久久 | 午夜国产福利在线观看 | 九九免费在线视频 | 国产二级视频 | 在线成人免费av | 456成人精品影院 | 亚洲免费色 | 日韩大陆欧美高清视频区 | 在线亚洲午夜片av大片 | 国产在线播放一区二区三区 | 国产二级视频 | 精品高清美女精品国产区 | 成人av资源在线 | 天天色天天操天天爽 | a v在线观看 | 欧美日韩国产在线一区 | 国产69久久久欧美一级 | 西西444www高清大胆 | 日韩小视频 | 五月综合色婷婷 | а天堂中文最新一区二区三区 | 久久资源在线 | 久草视频观看 | 97日日碰人人模人人澡分享吧 | 九色porny真实丨国产18 | 久久久受www免费人成 | 91黄视频在线观看 | 天天弄天天操 | 亚洲色图22p| 日韩精品一区二区在线观看视频 | 午夜视频一区二区三区 | 91在线91 | 午夜手机电影 | 亚洲我射av | 激情视频一区二区三区 | 久久96国产精品久久99漫画 | 午夜精品一区二区三区免费视频 | 亚洲成人精品久久久 | 黄色特级片 | 国内精品久久影院 | 高清久久久 | 一 级 黄 色 片免费看的 | 久久成人资源 | 日韩成人免费在线 | 亚洲精品视频一二三 | 狠狠狠色狠狠色综合 | 欧美在线视频一区二区三区 | 国产精品入口麻豆www | 久久免费精品国产 | 玖玖色在线观看 | 国产精品女人久久久 | 天天综合网国产 | 久久久久久久久久久黄色 | 亚洲精品自在在线观看 | 亚洲国内精品在线 | 欧美性黄网官网 | 99久久久国产精品免费99 | 国产美女精品视频 | 久久久久久久久久久网 | av大片网址 | 日本在线成人 | 国产亚洲精品久久久久久 | 日韩一级网站 | 日韩av一区二区在线 | 超级av在线 | 欧美福利视频一区 | 日日爽天天 | 午夜成人免费电影 | 亚洲国产精品va在线看黑人动漫 | 91精品久久久久久综合乱菊 | 91看片麻豆 | 国产1区2区3区在线 亚洲自拍偷拍色图 | 国产精品久久久久久久久久妇女 | 日本黄色免费播放 | 亚洲在线精品 | 夜夜躁狠狠躁日日躁 | 2024国产精品视频 | 精品国产乱子伦一区二区 | 最新日韩视频在线观看 | 蜜臀久久99精品久久久无需会员 | 日本丶国产丶欧美色综合 | 在线观看中文字幕亚洲 | 欧美一级乱黄 | 精品视频亚洲 | 特级黄色一级 | 天天天天天天天操 | 精品国产免费av | 伊人伊成久久人综合网小说 | 99在线精品免费视频九九视 | 国产群p| 成人黄视频 | 中文字幕第 | av在线播放中文字幕 | 国产视频 久久久 | 欧美久久久久久久久 | 黄色av电影网| 亚洲日本va午夜在线影院 | 贫乳av女优大全 | 成人免费xxx在线观看 | 天天天综合网 | 毛片3| 国产成人免费高清 | 久久久久国产精品午夜一区 | 国产美女久久 | 国产在线国产 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 久久99精品一区二区三区三区 | 国产免费又爽又刺激在线观看 | 国产精品中文字幕在线播放 | 日韩电影中文字幕在线 | av电影久久| 日韩精品一区在线观看 | 亚洲永久精品视频 | 精品亚洲一区二区三区 | 亚欧日韩av| 日本中文字幕在线看 | 99热这里只有精品在线观看 | 91成人免费| 久热只有精品 | 亚洲伦理精品 | 亚洲欧美国产精品18p | 特及黄色片 | 成年人国产在线观看 | 96久久久| 精品电影一区 | 久久久国产精品一区二区中文 | 激情久久综合 | 久久久久久久久久久成人 | 久久在线视频在线 | 欧美性超爽 | 99国产在线观看 | 国产91影院| 在线免费视频你懂的 | 99免费在线 | 丁香婷婷色 | 日韩一区视频在线 | 天天干天天玩天天操 | av黄色免费网站 | 久久久受www免费人成 | 毛片在线网 | 国产超碰在线观看 | 17videosex性欧美 | 婷婷综合在线 | 日本黄色免费大片 | 美女精品在线 | 成人免费视频网 | 国产一区在线播放 | 国产视频2 | 日韩中文字幕网站 | 人人澡人人爱 | 亚洲一级片在线看 | 欧美色插 | 久久成人国产精品 | 看片的网址 | 日本中文不卡 | 亚洲精品视频在线播放 | 国产色啪| 人人爽人人爽人人片av | 久草视频国产 | 日韩精品无码一区二区三区 | 婷婷色在线播放 | 亚洲一区日韩精品 | 亚洲日本中文字幕在线观看 | 一区视频在线 | 五月天久久综合 | 午夜国产一区 | 日韩免费一级a毛片在线播放一级 | 天天操天天色综合 | 丁香花在线观看免费完整版视频 | 国产一区二区手机在线观看 | 欧美天天综合 | 97综合网| 日韩成人免费在线电影 | av女优中文字幕在线观看 | 亚洲五月婷| 日韩精品中文字幕久久臀 | 国产黄色精品在线观看 | 中文字幕亚洲五码 | 人人爽人人香蕉 | 国内精品小视频 | 在线观看中文字幕亚洲 | 亚洲精品久久久久中文字幕二区 | 黄色小网站免费看 | 日韩精品欧美专区 | 久草在线资源免费 | 中文字幕在线色 | 国产色 在线 | 黄色a级片在线观看 | 国产日韩精品一区二区三区在线 | 日韩综合视频在线观看 | 久久久久国产a免费观看rela | 奇米网网址 | 在线成人欧美 | 久久精品中文视频 | 成人黄色小视频 | 国产私拍在线 | 欧美激情视频一区二区三区 | 精品国产视频在线观看 | 伊人影院99| 97网在线观看| 国产乱码精品一区二区蜜臀 | 九九电影在线 | 国内小视频在线观看 | 二区三区中文字幕 | 天天干天天插伊人网 | av大片网站 | 亚洲乱码久久 | 日韩中文字幕免费电影 | 国产在线无| 久久久久久国产精品亚洲78 | 国产精品自拍在线 | 久久久国产精品电影 | 午夜影院日本 | 亚洲黄网址 | 亚洲国产成人精品电影在线观看 | 在线免费国产视频 | 成人精品视频久久久久 | 亚洲精品www.| 欧美色黄| 天天色天天综合 | av在线免费网站 | 欧美性成人 | 欧美一级片免费观看 | 色av资源网| www黄com| 国产成人一区三区 | 欧美va日韩va | 日韩av免费在线看 | 中文字幕在线精品 | 久久视影 | 国产午夜麻豆影院在线观看 | 日韩精品中文字幕av | 天天操天天操天天操 | 亚洲国产精品成人va在线观看 | 99这里精品 | 久久免费av电影 | 欧美国产日韩一区 | 99国产免费网址 | 欧美日比视频 | 成人免费观看视频大全 | 色激情在线 | 色偷偷男人的天堂av | 亚洲闷骚少妇在线观看网站 | 四虎影视8848dvd | 日本免费久久高清视频 | 99久久精品免费看国产麻豆 | 九九九九色 | 夜夜爱av| 天天色天天操天天爽 | 麻花传媒mv免费观看 | 免费久久视频 | 久久久www成人免费毛片麻豆 | 9在线观看免费高清完整 | 高清免费av在线 | 天天操天天草 | 婷婷综合影院 | 91精选| 婷婷成人亚洲综合国产xv88 | 亚洲精品午夜久久久久久久 | 久久久久国产一区二区三区 | 亚洲香蕉视频 | 中文字幕国产一区 | 亚洲精品91天天久久人人 | 欧美动漫一区二区三区 | 婷婷在线看 | 日韩精品视频免费在线观看 | 亚洲国产精品第一区二区 | 中文字幕在线免费看线人 | www.久久精品视频 | 美女久久99| 91精品电影 | 午夜av在线播放 | 国产视频一区在线免费观看 | 亚洲蜜桃av| 中文字幕高清视频 | 久久视频99 | 免费网站黄 | 国产剧情av在线播放 | 色的网站在线观看 | 久久激情日本aⅴ | 久久久综合 | 日韩免费观看av | 中文字幕免费高清在线 | 韩国精品一区二区三区六区色诱 | 一区二区在线不卡 | 国产精品久久久视频 | 亚洲小视频在线 | 91九色蝌蚪视频 | 91av福利视频 | 日韩在线影视 | 五月婷婷综合网 | 81精品国产乱码久久久久久 | 在线免费视 | 九九久久久久久久久激情 | 国产亚洲精品久 | 一区二区三区在线播放 | 91精品国产九九九久久久亚洲 | 欧美性高跟鞋xxxxhd | 特级西西444www大胆高清无视频 | 久久久久亚洲精品国产 | a黄色片在线观看 | 操操操人人 | 国产香蕉97碰碰碰视频在线观看 | 日韩成人精品在线观看 | 亚洲永久精品一区 | 国产精品 9999 | 国产精品永久 | 韩国精品视频在线观看 | 免费三级网 | 99精品国产一区二区 | 欧美极品一区二区三区 | 欧美激情综合五月色丁香 | 亚洲精品国产精品国自 | 天天操天天干天天干 | 日韩在线一二三区 | 国产精品久久一卡二卡 | 亚洲天天综合网 | 最近中文字幕 | 国产视频精品视频 | 成人黄色大片在线观看 | 天天射天天射天天 | 色婷婷色 | 狠狠色丁香婷婷综合基地 | 夜色.com | 欧美激情视频在线免费观看 | 日本久久不卡视频 | 国产一二区免费视频 | 亚洲2019精品 | 成人小视频在线观看免费 | 日韩高清不卡一区二区三区 | 日日干天夜夜 | 三级av小说 | 亚洲h视频在线 | av一区二区在线观看中文字幕 | 日韩精品一二三 | 久久免费在线观看视频 | 亚洲精品午夜久久久久久久久久久 | 久久久久看片 | 91在线产啪| 日韩天堂在线观看 | 香蕉久草 | av在线官网 | 美女国产精品 | 国产精品视频大全 | 五月香婷 | 九九99| 亚洲三级精品 | 在线观看免费国产小视频 | 久久公开视频 | 一区二区欧美日韩 | 久久精品电影院 | 福利视频导航网址 | 97超碰人人干 | 99视频网站 | 久久久福利 | 91人人爽人人爽人人精88v | 久久精品久久久久久久 | 天天操天天弄 | 91精品国产欧美一区二区成人 | 性色在线视频 | 日本精品一区二区三区在线观看 | 欧美日韩精品在线视频 | 三级动图| 欧美日韩国产一区二 | 久久综合99| 国产精品欧美久久久久三级 | 国产成人av一区二区三区在线观看 | 久久桃花网 | 国产69久久 | 成片视频免费观看 | 国产剧情在线一区 | 人人爽人人 | 日韩激情在线视频 | 超碰人在线 | 国产在线观看一区 | 日韩av片免费在线观看 | 欧美日韩中字 | 天天av在线播放 | 日韩高清不卡一区二区三区 | 国产成人一区二区三区免费看 | 九九精品视频在线 | 国产成人av电影在线观看 | 日韩一区精品 | 国产精品美女www爽爽爽视频 | 久草色在线观看 | 91在线91| a色视频| 亚洲精品视频在线免费播放 | 天天色天天综合 | 精品久久国产 | 亚洲综合欧美激情 | 91精品国产综合久久久久久久 | 午夜精品视频一区二区三区在线看 | 激情偷乱人伦小说视频在线观看 | 国内精品久久久久久久久 | 亚洲最大免费成人网 | 欧美成人一二区 | 久久视频国产精品免费视频在线 | 91免费网| 国产a高清 | 一区二区三区在线不卡 | 久久成人国产精品一区二区 | 久久国产一区 | 麻花豆传媒一二三产区 | 在线观看视频h | 国产色爽 | 在线你懂 | 免费成人在线观看 | 国产福利免费在线观看 | 一级国产视频 | 日韩激情一二三区 | 成+人+色综合 | 欧美一二区视频 | 天天操操 | 日韩在线中文字幕 | 日韩免费观看一区二区三区 | 天天操天天射天天爽 | 免费av观看 | 日日噜噜噜噜夜夜爽亚洲精品 | 午夜国产一区二区三区四区 | 在线亚洲欧美视频 | 久久婷婷视频 | 四虎在线永久免费观看 | 在线观看视频福利 | 国产破处视频在线播放 | 少妇精品久久久一区二区免费 | 欧美日韩免费看 | 国产精品一区二区三区四 | 亚洲国产美女精品久久久久∴ | 日本三级中文字幕在线观看 | 黄色网在线免费观看 | 黄色大全免费网站 | 婷五月天激情 | 波多野结衣亚洲一区二区 | 国产成人l区 | 亚洲欧美视频 | www.99久久.com| 日韩精品一区二区三区免费视频观看 | 最近日本韩国中文字幕 | 亚洲黄色在线观看 | 久久久天天操 | 69av视频在线观看 | 欧美日韩国产一区二区在线观看 | 天天综合导航 | 日韩成人免费在线 | 久久手机免费观看 | 国产精品99久久久久久武松影视 | 在线综合 亚洲 欧美在线视频 | 综合色伊人 | 91大神一区二区三区 | 色综合久久久久综合99 | 在线观看av不卡 | 欧美日韩一区二区三区在线免费观看 | 欧美精品一区二区性色 | 新版资源中文在线观看 | 精品成人网 | 日本在线中文 | 黄www在线观看 | 久久视频国产精品免费视频在线 | 天天操天天操天天爽 | 亚洲激情一区二区三区 | 福利视频导航网址 | 精品国产免费人成在线观看 | 久久精品官网 | 亚洲伊人网在线观看 | 人人爱在线视频 | 日女人免费视频 | 亚洲美女在线一区 | 国产偷国产偷亚洲清高 | 不卡av电影在线观看 | 香蕉网站在线观看 | 中文字幕av在线不卡 | 婷婷四房综合激情五月 | 天天弄天天干 | 成人免费在线观看电影 | 五月天婷亚洲天综合网精品偷 | 日本久久电影网 | 亚洲年轻女教师毛茸茸 | 国产流白浆高潮在线观看 | 欧美日韩国产一区二区三区在线观看 | 天天爽天天摸 | 国产美女免费视频 | 国产精品一区一区三区 | 欧美成人精品三级在线观看播放 | 婷婷综合视频 | 中文字幕精品一区久久久久 | 99久久精品国产一区 | 精品国产精品国产偷麻豆 | 日韩亚洲在线观看 | 91精品在线免费观看视频 | 色婷婷一区| 在线成人中文字幕 | 欧美久久久久 | 日韩深夜在线观看 | 日韩一区二区三区高清在线观看 | 精品在线二区 | 国产高清视频免费最新在线 | 手机av在线网站 | 丝袜美女视频网站 | 2017狠狠干 | 国产一区二区视频在线 | 99热这里只有精品在线观看 | 久久99精品久久久久婷婷 | 国产97免费 | 在线观看视频三级 | 99电影| 国产精品一区二区三区免费视频 | 伊人天堂网 | 久久精品视频免费观看 | 中文字幕在线视频精品 | 日本夜夜草视频网站 | 天天操夜夜叫 | 一级国产视频 | www最近高清中文国语在线观看 | 欧美日产在线观看 | 午夜在线日韩 | 亚洲 精品在线视频 | av黄色在线观看 | 国产精品久久久久久久久搜平片 | 一区二区三区在线观看免费 | 色偷偷97| 永久免费的av电影 | 国产涩涩在线观看 | 久久一区国产 | 国产精品久久久久永久免费看 | 亚洲精品乱码久久久久久按摩 | 日韩在线免费高清视频 | 国产精品久久久久久一区二区三区 | 国产精品小视频网站 | 成人精品国产免费网站 | 麻豆视频观看 | 日韩欧美v| 国产 欧美 在线 | 精品亚洲男同gayvideo网站 | 日本动漫做毛片一区二区 | 麻豆一二三精选视频 | 久久开心激情 | 色综合天天综合 | 久久久久一区二区三区四区 | 免费看一级片 | aaawww| 人人干网 | 蜜桃视频在线观看一区 | 久久99热这里只有精品 | 九九av| 91久久精品日日躁夜夜躁国产 | 婷婷色婷婷 | 91女子私密保健养生少妇 | 精品在线免费视频 | 亚州激情视频 | 在线看中文字幕 | 日本最大色倩网站www | 国产韩国日本高清视频 | 美女久久久久 | 国产精品久久久久久久免费大片 | 丁香婷婷综合激情 | 很污的网站 | 免费福利视频网站 | 麻豆国产电影 | 最新黄色av网址 | 日韩三级视频在线观看 | 狠狠操天天操 | 国产精品久久久一区二区 | 国产精品99页| 91热爆在线观看 | 欧美日韩中文字幕视频 | 免费看的黄色 | 99久久影视 | 黄色一级大片在线免费看产 | 黄色在线视频网址 | 久久久久成人免费 | 少妇精品久久久一区二区免费 | 黄色一级大片免费看 | 9999在线视频 | 亚洲精品乱码久久久久久9色 | 久久精品久久久久 | 国产视频九色蝌蚪 | av在线不卡观看 |