(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();
}
}

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. PSO(粒子群算法)MATLAB仿真完整代码

    %程序1:PSO.m文件 %------ 基本粒子群优化算法( Particle Swarm Optimization ) ----------- %------ 功能:求解无约束问题 %------ 调用格式:[xm,fv] =PSO(fitness,N,c1,c2,w,M,D) function [xm,fv] =PSO(fitness,N,c1,c2,w,M,D) %fitness:待优化的目标函数 %N:粒子数目…...

    2024/4/24 15:50:34
  2. 二级c语言基础题库(1)

    就不抄原题了,太浪费时间,直接将选项以判断题的形式写出来,并给出错误原因。第1题:C语言中每条可执行语句和非执行语句最终都将被转换成二进制的机器指令。错误:C语言中的非执行语句不会被编译,不会生成二进制的机器指令。C语言源程序经过C语言编译程序之后生成一个后缀为…...

    2024/4/12 7:37:51
  3. onchange与onpropertychange的联系与区别!

    http://www.cnitblog.com/yemoo/archive/2006/08/19/15585.html先看这么一段解释:当一个HTML元素的属性改变的时候,都能通过 onpropertychange来捕获。例如一个<input name="text1" id="text1" />对象的value属性被页面的脚本修改的时候,onchange…...

    2024/4/12 7:37:39
  4. Golang教程:(五)常量

    原文:https://golangbot.com/constants/这是本Golang系列教程的第五篇。定义常量常量(constant)表示固定的值,比如:5,-89,"I love Go",67.89 等等。考虑如下程序:var a int = 50 var b string = "I love Go"上面的程序中, a 和 b 分别被赋值为…...

    2024/4/13 10:03:40
  5. 可以查看java字节码的eclipse插件

    Bytecode Outline :它是Eclipse的插件,可以把当前的正在编辑Java的文件或者class文件直接显示出其相应的字节码出来,而且可以进行两个Java文件的字节码比较或者两个class文件的字节码比较或一个Java文件与一个class文件进行字节码的比较。 方法1. 直接拷贝下面两个类到eclip…...

    2024/4/12 14:07:01
  6. 小型永磁直驱风力发电机MPPT控制器开发

    项目内容本项目针对实验室的一台200W小型永磁直驱风力发电机,搭建由小型永磁直驱风力发电机、送风机、不可控整流器、Boost电路、转速传感器、负载等构成的硬件试验系统。构造出大量功率-转速-有效风速样本,建立基于人工神经网络建立有效风速估计模型。利用有效风速估计模型进…...

    2024/4/20 2:31:00
  7. golang视频教程

    - 《Go编程基础》 Unknwon/go-fundamental-programming GitHub - 《Go Web基础》 Unknwon/go-web-foundation GitHub - 《Go名库讲解》 Unknwon/go-rock-libraries-showcases GitHub作者:无闻Unknwon 链接:https://www.zhihu.com/question/23486344/answer/24773732 来…...

    2024/4/12 22:33:43
  8. 硬盘的爱情故事

    我是一个硬盘。 在一个普普通通的台式机里工作。别人总认为我们是高科技白领,工作又干净又体面,似乎风光得很。也许他们是因为看到洁白漂亮的机箱才有这样的错觉吧。其实 象我们这样的小台式机,工作环境狭迫,里面的灰尘吓得死人。每天生活死水一潭,工作机械重复。跑跑文字…...

    2024/4/18 22:24:30
  9. VS2010——ODBC方式连接MYSQL数据库

    因为一个项目需要从远程数据库上读取数据,所以从网上搜了大量的教程,但是就算跟着教程依然出了很多问题,我在这里记录一下出现的错误以及解决办法。 环境:win10 64位 + vs2010 64位 + mysql 5.7 软件:mysql-connector-odbc-5.1.13-win32 mysql-connector-odbc-5.1.13-wi…...

    2024/4/18 9:58:15
  10. MPPT项目总结

    项目中使用的设计模式:单态模式,责任链模式,DAO模式,装饰模式,门面模式,状态模式,策略模式等。项目开发中的优秀实践经验总结:需求功能点的跟踪(需求跟踪矩阵)涵盖整个项目开发周期的各个阶段,这样可以确保需求不会遗漏,也不会产生较大的需求偏差;需求功能点的优先级…...

    2024/4/16 13:19:33
  11. 选择图片,即时浏览onpropertychange的设置!

    <input name="file" type="file" id="file" style="width:250px" onpropertychange="javascript:document.getElementById(preview).src=this.value" />以下asp.net2.0设置方法:<asp:FileUpload ID="FileUplo…...

    2024/4/23 1:53:50
  12. ASM(字节码处理工具)

    ASM是一种Java字节码生成和分析的框架,我们可以用它通过操作二进制的方式来修改现成的类或者动态生成class文件。ASM不仅提供了和其他字节码生成框架相类似的功能,此外它还关注框架的易用性和性能。ASM的源代码中包含了若干packages: org.objectweb.asm:是项目的核心包,它…...

    2024/4/16 17:23:54
  13. 【转】Go语言入门教程(一)Linux下安装Go

    说明 系统是Ubuntu。 关于安装下载安装包 当前官方下载地址是https://golang.org/dl/,如果不能访问,请自行FQ,FQ是技术工作者的必备技能。 安装 tar -xzvf go1.8.3.linux-amd64.tar.gz mv go/ /usr/local go1.8.3.linux-amd64.tar.gz是编译过的,解压即完成安装 上述两个步…...

    2024/4/13 21:50:08
  14. WIN7管理工具配置ODBC数据源-系统DSN中无Oracle,Sybase驱动的解决方法

    在C:\Windows\SysWOW64下找到: odbcad32.exe 这个文件,双击打开。 点击添加按钮,选择 对应的 驱动,然后就可用添加连接Oracle/Sybase的ODBC的数据源了。...

    2024/4/16 1:56:14
  15. Javassist (字节码修改工具)用法

    Javassist简介javassist是一个开源的分析、编辑和创建Java字节码的类库。可以对.class进行直接修改和创建,直接使用java编程的方式,不需要了解虚拟机的指令,就能动态的改变类的结构或者动态生成Javassist 应用场景应用性能的监控、动态代理等重要类简介:ClassPool:javassi…...

    2024/4/20 0:38:03
  16. 求指教,PSO算法跟踪光伏电池最大功率点

    底下是粒子群算法代码和子程序: %------初始格式化-------------------------------------------------- clear all; clc; format long; %------给定初始化条件--------------------------------------------- c1=2; %学习因子1 c2=2; %学习因子2 w_m…...

    2024/4/22 9:23:36
  17. HTML元素的onpropertychange的作用

    最近在做一个.NET项目的时候,需要响应TextBox控件的OnTextChange事件,但遇到的一个问题是:当我通过页面上的javascript来改变TextBox控件值的时候,并不触发OnTextChange。也就是我们通过程序而不是通过页面响应,为TextBox改变值,这时候OnTextChange是不起作用的。想了想,…...

    2024/4/16 1:56:55
  18. Oracle配置数据源

    文章目录Oracle配置数据源 Oracle配置数据源 学习PowerDesiger时需要配置Oracle数据源,结果发现没有Oracle相关,后来才知道需要提前配置,SQL Server,Access同理类似 配置步骤如下打来ODBC管理器,注意需要用管理员方式运行选择系统DSN,点击添加配置Orale 数据源 选择安装的…...

    2024/4/26 9:28:23
  19. Go(Golang)安装教程

    个人推荐采用源码安装或者第三方工具安装。一、源码安装1.设置Go的环境变量GOROOT_BOOTSTRAP 这个目录在安装 Go 1.5 版本及之后的版本时需要设置。由于在 1.4 版本后,Go 编译器实现了自举,即通过 1.4 版本来编译安装之后版本的编译器。如果不设置该环境变量的话,会产生这样…...

    2024/4/14 21:34:12
  20. Java查看字节码的4种方式

    JDK的javap工具Intellij idea快速查看Java类字节码:转载自http://allan.li/intellij-ideakuai-su-cha-kan-javalei-zi-jie-ma/IDEA插件在Intellij idea插件管理里面安装jclasslib bytecode viewer插件。然后打开一个java文件,在底下的面板上View-Show bytecode with jclassli…...

    2024/4/5 0:22:05

最新文章

  1. kotlinDSL控制的安卓项目导入已存在的模块后sync报错

    原因很明显&#xff0c;但是我还找了好久 因为在import时并没有选择groove还是kotlin控制&#xff0c; 所以默认为groovy控制的&#xff0c;然而主项目是由kotlin dsl控制的grale行为。 原因清楚之后&#xff0c;就可以去检查一下&#xff0c;项目里是否包含了settings.gradle和…...

    2024/5/1 6:22:21
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 腾讯云轻量服务器流量不够用了会怎么样?

    腾讯云轻量应用服务器是限制月流量的&#xff0c;如果当月流量不够用了&#xff0c;流量超额了怎么办&#xff1f;流量超额后&#xff0c;需要另外支付流量费&#xff0c;如果你的腾讯云账号余额&#xff0c;就会自动扣除对应的流量费&#xff0c;如果余额不足&#xff0c;轻量…...

    2024/4/30 5:18:15
  4. 【Easy云盘 | 第十三篇】分享模块(获取目录信息、获取文件信息、创建下载链接)

    文章目录 4.4.7获取目录信息4.4.8获取文件信息4.4.9创建下载链接 4.4.7获取目录信息 明天做 4.4.8获取文件信息 明天做 4.4.9创建下载链接 明天做...

    2024/4/30 1:48:29
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/29 23:16:47
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/30 18:14:14
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/30 18:21:48
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/30 9:43:22
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57