Fork me on GitHub

排序算法--java实现

一.冒泡排序

算法原理:

给定一个数组,从小到大排序

  1. 数组头部开始比较相邻的两个元素,如果头部的元素比后面的大,就交换两个元素的位置。
  2. 往后对每个相邻的元素都做这样的比较、交换操作,这样到数尾部时,第 1 个元素会成为最小的元素。
  3. 重新从头部开始第 1、2 步的操作,除了在这之前头部已经排好的元素。
  4. 继续对越来越少的数据进行比较、交换操作,直到没有可比较的数据为止,排序完成。
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package BubbleSort;

/**
* 冒泡排序
*/

public class BubbleSort {

public static int[] bullueSort(int[] array) {
if (array.length == 0) {
return array;
}
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length - 1; j++) {
if (array[j + 1] < array[j]) {
int temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}

}

}
return array;

}

public static void main(String[] args) {
int [] arr = {0,41,5,5,866,47};
int [] sorted = bullueSort(arr);
for (int i:sorted
) {
System.out.print(i+" ");
}

}
}

二.选择排序

算法原理:
  1. 在未排序的数组中找到最大(小)元素,存放到排序序列的起始位置
  2. 在剩余的序列中继续寻找最大(小)元素存放到已排序的序列末尾
  3. 重复上述步骤,直到所有的元素都排序完成,即可得到从大(小 )到小(大)的序列
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package SelectionSort;

/**
* 选择排序
*/

public class selectionSort {
public static int[] selectionSort(int[]array){
if(array.length == 0){
return array;
}
for(int i=0;i<array.length;i++){
int minIndex = i;
for(int j=i;j<array.length;j++){
if(array[j]<array[minIndex]){//寻找最小的数
minIndex = j;//保存最小值的角标
}
/**
* 交换最小值与无序序列的第一位
*/

int temp =array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
}
}

return array;
}


public static void main(String[] args) {
int [] arr = {0,41,45,5,45,47};
int [] sorted = selectionSort(arr);
System.out.println("选择排序如下:");
for (int i:sorted
) {
System.out.print(i+" ");
}
}
}

三.插入排序

算法原理:
  1. 从第一个元素开始,该元素被认为是已排序元素
  2. 取出下一个元素,在已排序的元素序列中从后向前进行扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一个位置
  4. 重复步骤三,直到找到已排序元素小于或等于新元素的位置
  5. 将新元素插入到找到的元素后边
  6. 重复上述2-5步骤
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package InsertionSort;
/**
* 插入排序

*/
public class InsertionSort {

public static int[] insertionSort(int[] array){
if(array.length == 0){
return array;
}
int current;
for (int i =0;i<array.length-1;i++){
current =array[i+1];
int preIndex =i;
while (preIndex>=0&&current<array[preIndex]){//保证preIndex>0,确保数组不会越界
array[preIndex+1] =array[preIndex];
preIndex--; //向前寻找小于等于当前元素的值
}
array[preIndex+1]= current;
}
return array;
}


public static void main(String[] args) {
int [] arr = {0,41,45,5,45,47};
int [] sorted = insertionSort(arr);
System.out.println("插入排序如下:");
for (int i:sorted
) {
System.out.print(i+" ");
}
}
}

四.希尔排序

算法原理:

算法背景:希尔排序是Donald Shell 于1959年提出的一种排序算法。希尔排序也是一种插入排序,所以想要掌握希尔排序,首先要了解插入排序,请会上文排序算法三。希尔排序是简单插入排序经过改进后的一个更加高效的版本,也称为缩小增量排序。其是时间效率是突破O(N2)的第一批算法之一。
**·

希尔排序把数据序列按照一定的增量进行分组,对每组使用直接插入排序算法;随着增量逐渐减少,每组包含的关键词原来越少,当增量减到1️⃣的时候,整个序列就会被分成一组,进行插入排序,算法便终止了

  1. 选择典型的希尔增量将原来的序列划分为若干个增量序列(关于选择怎样的增量进行划分涉及到算法的性能,所以其选择和证明是一个较为复杂的数学难题),在此我们选择{n/=2…1}增量序列
  2. 按照增量序列的个数k,对序列进行k次插入排序
  3. 每次排序,根据对应的增量,将待排序的序列分割成若干个长度为m的子序列,并直接使用插入排序进行排序,仅当增量为1️⃣时,将整个序列看成一个序列进行处理。
    算法过程演示:
    希尔排序算法过程演示
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package ShellSort;

/**
* 希尔排序
*/
public class ShellSort {
public static int[] shellSort(int[]array){
if(array.length==0){
// System.out.println("数组为空");
return array;
}
int len = array.length;
int temp,gap = len/2;
while (gap>0){
for (int i = gap; i <len ; i++) {
temp = array[i];
int preIndex =i-gap;
while (preIndex>=0&&array[preIndex]>temp){//分组交换,(插入排序)
array[preIndex+gap]=array[preIndex];
preIndex -= gap;

}
array[preIndex+gap] = temp;
}
gap/=2;//缩小步长,继续跳入第一层while 循环
}



return array;
}
public static void main(String[] args) {
int [] arr = {465,56,4,4,5,334,7,7,165};
int [] sorted = shellSort(arr);
System.out.println("希尔排序如下:");
for (int i:sorted
) {
System.out.print(i+" ");
}
}
}

五.归并排序

算法原理:

算法背景:和选择排序一样,归并排序的性能不受输入数据的影响,但是其时间性能的表现比选择排序好的多,始终为O(nlog n)但是其代价是需要额外的内存空间
·
归并排序是采用分治法的一个非常典型的应用,归并排序是一种稳定的排序算法,将已经有序的序列进行合并,得到完全有序的序列。
·
因为较难理解 ,此处整一个讲得很好的B站视频链接:归并排序

  1. 把长度为n的输入序列分为两个长度为n/2的子序列
  2. 对这两个子序列进行分别的归并排序
  3. 将两个排序好的子序列合并成一个最终的排序序列(其实是把递归的过程交给了计算机)
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package MergeSort;

import java.util.Arrays;

/**
* 归并排序
*/
public class MergeSort {

public static int[] mergeSort(int[]array){

if(array.length<2){
return array;
}
int mid = array.length/2;
int []left = Arrays.copyOfRange(array,0,mid);
int []right = Arrays.copyOfRange(array,mid,array.length);
return merge(mergeSort(left),mergeSort(right));



}
/**
*
* 归并排序--将 两段排序好的数组合成一个排序数组
*
*/
public static int[]merge(int[]left,int[]right){
int[] result = new int[left.length+right.length];//新建一个可以容纳左右数组的新数组
//两个自列的归并过程
for (int index = 0,i=0,j=0;index<result.length;index++){
if(i>=left.length){
result[index]=right[j++];

}else if(j>=right.length){
result[index] = left[i++];

}else if(left[i]>right[j]){
result[index]=right[j++];
}else {
result[index]=left[i++];
}
}
return result;
}

public static void main(String[] args) {
int [] arr = {465,56,4,4,5,334,7,7,165};
int [] sorted = mergeSort(arr);
System.out.println("归并排序如下:");
for (int i:sorted
) {
System.out.print(i+" ");
}
}
}

六.快速排序

算法原理:

算法背景:快速排序通过一趟排序将序列分割成独立的两部分,其中一部分均比关键字小,一部分均比关键字大,然后对分割出来的两部分继续进行快速排序的递归调用,以达到使整个序列有序的目的

  1. 从数列中挑选一个元素,将其称为‘基准’,为方便实现,我们总将最左端的元素设置为基准
  2. 重新排序数列,将所有的比基准值大的放在基准数的后边,比基准数小的放在基准数的前边(相同的数可以放在任意边),在这个分区操作退出后,基准数就交换到分区结束的位置
  3. 递归地把分区之后大于和小于基准数的子序列进行排序
代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package QuickSort;

/**
* 快速排序方法
*/
public class QuickSort {

public static void quickSort(int[] array, int left, int right) {
//如果左边的索引大于右边的索引,出现错误,直接退出该排序方法
if (left > right) {
return;
}
//确定基准数,以最左开始
int base = array[left];
int i = left;
int j = right;
while (i != j) {
while (array[j] >= base && i < j) {
j--;
}
while (array[i] <= base && i < j) {
i++;
}

//代码走到这里意味着在基准条件下找到了符合条件的元素,交换他们
int temp = array[i];
array[i] = array[j];
array[j] = temp;

}
//i=j此时索引走到一起,交换基准数和相遇位置元素
array[left] = array[i];
array[i] = base;
//一趟快排后继续排左边
quickSort(array, left, j - 1);
quickSort(array, j + 1, right);

}


public static void main(String[] args) {
int[] arr = { 0, 41, 45, 5, 45, 47};
quickSort(arr, 0, arr.length - 1);
for (int i : arr
) {
System.out.print(i + " ");
}

}

}

七.堆排序

算法原理:

堆排序是指利用堆这种数据结构所设计的排序算法。堆是一个近似于完全二叉树的结构,同时满足其自身子节点键值或索引总是小于或大于其父节点
此处墙裂推荐B站良心up视频:传送门

  1. 逐个构建大顶堆
  2. 对构建的大顶堆的父节点进行遍历,以此来得到根节点最大的堆
  3. 交换最后一个元素和根节点,此时相当于拿到了取走最大值后的序列的最大值
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//代码注释很详细,有助于理解
package HeapSort;

/**
* 堆排序
*/
public class HeapSort {


private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

private static void heapify(int[] tree, int n, int i) {
if (i >= n) {//如果根节点大于节点的个数,直接跳出,结束递归
return;
}
int c1 = 2 * i + 1; //当前选定根节点的左子节点
int c2 = 2 * i + 2; //当前选定根节点的右子节点
int max = i; //假定当前根节点是该子堆的最大值
// * 找出子堆中的最大值,交换index
if (c1 < n && tree[c1] > tree[max]) {
max = c1; //如果左子节点大于根节点元素,交换其下标
}
if (c2 < n && tree[c2] > tree[max]) {
max = c2; //如果右子节点大于子堆中最大值,交换下标
}
if (max != i) {//如果最大值不是根节点,交换找到的最大值和根节点元素
swap(tree, max, i);//交换函数
heapify(tree, n, max); //递归做heapify,此时如果在主函数中 进行调用只对指定根节点的子堆进行heapifY操作
}
}

/**
* 对任意数组建立大顶堆
*
* @param tree
* @param n
*/

private static void build_heap(int[] tree, int n) {
int last_node = n - 1;//找出完全二叉树的最后一个节点
int parent = (last_node - 1) / 2;//找出最后一个节点的父节点
for (int i = parent; i >= 0; i--) {//对树中的所有父节点进行遍历并heapify来得到根节点最大的堆
heapify(tree, n, i);
}

}


private static void heap_sort(int[] tree, int n) {
build_heap(tree, n);//把数组建立成一个大顶堆
int i;
for (i = n - 1; i >= 0; i--) {
swap(tree, i, 0);//遍历节点交换最后一个元素和堆最顶端根节点元素
heapify(tree, i, 0);//交换完成后相当于拿走交换的根节点(最大)再进行heapify操作
}
}

public static void main(String[] args) {
int[] arr = {4, 5, 8, 2, 10, 2};

heap_sort(arr, arr.length);//heapsort操作后遍历,就相当于从小到大遍历数组
for (int i : arr) {
System.out.println(i);
}

}

}

八.计数排序

算法原理:
  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  3. 对所有的计数累加(从C的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组,将每个元素i放在新数组的第C(i)项,放一个就减去1
    代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package CountingSort;

import java.util.Arrays;

public class CountSort {
public static int[] CountingSort(int[] array) {
if (array.length == 0) {
return array;
}
int bias, min = array[0], max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {//找出未排序数组中最大的数
max = array[i];
}
if (array[i] < min) {//找出未排序数组中最小的数
min = array[i];
}
}
bias = 0 - min; //确定位差
int[] bucket = new int[max - min + 1]; //申请一个最大值与最小值范围个数的新数组
Arrays.fill(bucket, 0);//初始化数组,使用Arrays 将初始值0全部复制,省略了繁琐的for循环
for (int i = 0; i < array.length; i++) {
bucket[array[i] + bias]++; //将对应的数据加上位差后在新数组上对应后计数,并将计数结果保存在新数组中
}
int index = 0, i = 0;//初始化数组的索引
while(index<array.length){
if(bucket[i] != 0){
array[index] = i - bias; //将新数组的下标加上位差后重新填充到待排序数组
bucket[i]--; //每取出一个将该位置上的计数减一
index++; //待排序数组的下表索引加一
}else{
i++; //如果将新数组i索引上的计数清零,就将新数组下标i索引+1
}
}
return array;//返回重新填充的待排序数组 ,此时该数组已经有序

}
public static void main(String[] args) {
int [] arr = {2,4,8,5,3};
int [] sorted = CountingSort(arr);
for (int i:sorted
) {
System.out.print(i+" ");
}

}

}

总结

各种算法的时间空间复杂度

github地址请见:Yuhan ‘s blog

Maven项目连接Oracle数据库坐标无法导入问题

问题:maven项目连接oracle数据库时,由于版权问题,依赖导入失败

解决方案:
1. 下载jar包(由于官网下载速度较慢,附百度云连接)

链接:oracle6.jar 提取码: 8sic

2. 将jar包放在maven仓库,在项目的maven cmd line中执行下面的命令。
1
2

install:install-file -Dfile=E:\maven_repository\ojdbc6.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=1.0.0 -Dpackaging=jar -DgeneratePom=true

如图所示:
按顺序操作

3.在pom文件中添加依赖
1
2
3
4
5
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>1.0.0</version>
</dependency>

至此依赖导入成功。

递归经典例题(赶鸭子)

一. 题目分析

①一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?

由题目可得知当经过第八个村庄时鸭子数量为2,所以利用已知条件由后向前递归计算可较为简便计算出出发时的总数,即第一个村庄的数量。由题意可轻松计算每个村庄卖出的数量。

②角谷定理。输入一个自然数,若为偶数,则把它除以2,若为奇数,则把它乘以3加1。经过如此有限次运算后,总可以得到自然数值1。求经过多少次可得到自然数1。
如:输入22,
输出 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
STEP=16

由题意可知应将输入数据分为1,偶数,奇数三种情况,分别进行分支处理。当经过选择递归后,当n 变为1后,程序到达递归出口。

二. 算法构造

在这里插入图片描述
在这里插入图片描述

三. 算法实现

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

public class Sellduck {
public static int duckNum(int n ) {

if(n==8) {//递归出口
return 2;//第七个村庄以后 剩下两只

}
else {

return 2*(duckNum(n+1)+1);//递归调用
}

}
public static void SellDuck(int n) {//剩下的鸭子数n 调用时输入2
int count = 7;
for(;count >= 1;count--) {
System.out.println("第"+count+"个村子卖的鸭子数为:"+(n+2));
n = (n+1) * 2;
}
System.out.println("他开始有的鸭子数为:"+n);
}
public static void main(String[] args) {

int num = duckNum(1);

System.out.println("总共赶鸭子"+num+"只");
for(int i=1 ;i<=7;i++) {//循环计算每个村庄的具体数目
int all = duckNum(i);
int sale= all/2+1;//计算卖出鸭子数
int remain= all-sale;
System.out.println("经过第"+(i)+"个村庄时卖了"+sale+"只鸭子,还剩"+remain+"只");
SellDuck(2);
}
}

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

public class valley_number {
static int count=0;
public static void vall_Num( int n) {


if(n==1) {
count++;
System.out.println(n);//输出1️后停止递归
System.out.println("step:"+count);
return ;//计算结果得到1时到达递归出口

}

if(n%2==1) {
count++;
System.out.print(n+" ");
n=n*3+1;//执行计算结果为奇数时的表达式
vall_Num(n);//递归


}else {
count++;
System.out.print(n+" ");

n=n/2;//执行计算结果为偶数时的表达式
vall_Num(n);
}


}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("please input a int Number");
int n= sc.nextInt();
vall_Num(n);
}
}

四. 调试测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五. 经验总结

通过编程掌握了递归程序设计的方法,更深理解了递归的概念,掌握了递归数学模型的建立,及递归程序和非递归程序之间的转换。具体递归问题分析时应先找出递归关系和递归出口。
   相同问题下,利用递归求解可减少代码量,但是从性能角度来看,递归函数调用自身 ,将会产生时间和内存空间的消耗,同时也有可能存在一些重复计算,这就造成了效率不高的问题。当调用层次过多时还有可能导致 出现栈溢出的状况。

单词小助手完善

一. 题目分析

  1. 在原有代码基础上添加文档注释,函数注释及语句注释,使代码风格良好,易于阅读和修改。
  2. 完善功能,实现背单词时出错词的重复记忆。
  3. 在词库维护选项中添加中英文分别查询的功能
  4. 完成输如数据的正确性验证,如:在菜单选择时输入不规范的错误提示、在添加单词时英文输入时输入汉语的异常排除等

二. 关键算法构造

1. 总菜单项目选择及maintain函数子选项的输入异常检查及提示。
在这里插入图片描述

在这里插入图片描述
2. 添加单词
在这里插入图片描述
在这里插入图片描述

三. 程序实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
#include <string.h>
#include <stdlib.h>
#define MAX_CHAR 20 // 最大字符
#define MAX_NUM 200 // 单词的最大个数
struct word
//单词的结构体
{
char en[MAX_CHAR]; // 英文形式
char ch[MAX_CHAR]; //中文形式
} s[MAX_NUM],r[MAX_NUM]; //单词数组
int num; //词典单词个数
int rnum = 0; //增强记忆单词个数
int select=1;//select 为是否退出系统的标记
int d=0,c=0;//c为答错的次数,d为答对的次数
//帮助
void help()
{
printf("\n本系统主要实现英语单词学习的功能。用户可对词典文件中的单词进行预览,增删改查。");
printf("\n同时还可进行中英、英中测试。本系统还提供了测试成绩的显示功能。");
}
//从文件中读取单词的信息
void readfile()
{
FILE *fp;
int i=0;
fp=fopen("data.txt","r");
if(!fp)
{
printf("\n打开文件data.txt失败!");
}
while(fscanf(fp,"%s %s ",s[i].en,s[i].ch)==2)
{
i++;
}
num=i;
if(0==i)
printf("\n文件为空,请选择词典维护增加词条!");
else
printf("\n");
fclose(fp);
}
//向文件中写入单词
void writefile()
{
FILE *fp;
int i=0;
fp=fopen("data.txt","w");
if(!fp)
{
printf("\n打开文件data.txt失败!");
}
for(i=0;i<num;i++)
{
fprintf(fp,"\n%s %s ",s[i].en,s[i].ch);
}
printf("\n");
fclose(fp);
}
//从增强记忆文件中读取单词
void readRemeberfile()
{
FILE *fp;
int i=0;
fp=fopen("Remeberdata.txt","r");
if(!fp)
{
printf("\n打开文件Remeberdata.txt失败!");
}
while(fscanf(fp,"%s %s ",r[i].en,r[i].ch)==2)
{
i++;
}
rnum=i;
if(0==i)
printf("\n你还没有错题哦!");
else
printf("\n");
fclose(fp);
}
void sort()/*按字典排序*/
{
int i,j;
char temp[MAX_CHAR];
for(i=0;i<num-1;i++)
{
for(j=num-1;j>i;j--)
if(strcmp(s[j-1].en,s[j].en)>0)//string类strcmp函数通过Ascll 码逐字符比较
{
strcpy(temp,s[j-1].en);
strcpy(s[j-1].en,s[j].en);
strcpy(s[j].en,temp);
strcpy(temp,s[j-1].ch);
strcpy(s[j-1].ch,s[j].ch);
strcpy(s[j].ch,temp);

}
}
}
//添加单词信息
void add()
{
int i=num,j,flag=1,a;
f: while(flag)
{
flag=0;
printf("\n请输入单词的英文形式:");
scanf("%s",s[i].en);
//strncpy(c,s[i].en.c_str(),s[i].en.length());
for(a=0;a<20;a++)
{if(s[i].en[a]>>8==0)
{
continue;
}
else
{printf("输入不是纯英文,请重新输入");
flag=1;
goto f;//当不是纯英文输入时跳转
}
}
//if(a!=19) continue;
for(j=0;j<i;j++)
if(strcmp(s[i].en,s[j].en)==0)//与已有单词比较
{
printf("已有该单词,请检查后重新录入!\n");
flag=1;
break; /*如有重复立即退出该层循环,提高判断速度*/
}

}
printf("\n请输入单词的中文形式:");
scanf("%s",s[i].ch);
num++;
printf("\n您输入的信息为: 英文: %s 中文: %s ",s[i].en,s[i].ch);
sort();
}
//删除单词信息
void del()
{
int i=0,j=0;
char en[MAX_CHAR];//英文形式
printf("\n请输入你要删除的单词英文形式:");
scanf("%s",en);
for(i=0;i<num;i++)//先找到该英文形式对应的序号
if(strcmp(s[i].en,en)==0)//判断已有单词中是否存在此词
{
for(j=i;j<num-1;j++)
s[j]=s[j+1];
num--; //数量减少 1
return;
}
printf("\n没有这个单词!");

}
//修改单词信息
void modify()
{
int i=0,choose=0,flag=1;//chooses代表选项标识,flag代表是否找到单词
char en[MAX_CHAR]; //英文形式
while(flag||choose)
{
printf("\n请输入你要修改的单词英文形式:");
scanf("%s",en);
for(i=0;i<num;i++)//先找到该英文形式对应的序号
if(strcmp(s[i].en,en)==0)
{
printf("\n请输入单词正确的英文形式:");
scanf("%s",s[i].en);

printf("\n请输入此单词正确的的中文形式:");
scanf("%s",s[i].ch);

printf("\n继续修改请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;
}
flag=0;

}
if(!flag)
printf("\n没有这个单词!");
}
//单词预览
void show()
{
int i=0;
printf("\n单词: 英文 中文 ");//格式对齐
for(i=0;i<num;i++)
printf("\n %-15s%-13s",s[i].en,s[i].ch);//单词预览
}
//查询单词
void search()
{
int i=0,choose=0,flag=1;
char ch[MAX_CHAR];//中文形式
while(choose||flag)
{
printf("\n请输入你要查询的单词中文形式:");
scanf("%s",ch);
for(i=0;i<num;i++)//先找到该中文形式对应的序号
if(strcmp(s[i].ch,ch)==0)
{
printf("\n英文形式 中文形式 ");
printf("\n %-12s%12s",s[i].en,s[i].ch);

printf("\n继续查询请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;}
flag=0;
}
if(!flag) printf("\n没有这个单词!");
}
//查询单词(英)
void ysearch()
{
int i=0,choose=0,flag=1;
char en[MAX_CHAR]; //英文形式
while(choose||flag)
{
printf("\n请输入你要查询的单词英文形式:");
scanf("%s",en);
for(i=0;i<num;i++)//先找到该中文形式对应的序号
if(strcmp(s[i].en,en)==0)
{
printf("\n英文形式 中文形式 ");
printf("\n %-12s%12s",s[i].en,s[i].ch);

printf("\n继续查询请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;
}
flag=0;
}
if(!flag) printf("\n没有这个单词!");
}
//中译英测试
void zytest()
{
char b1[20];
int z;
int choose=1;
int i;
FILE *fp;


while(choose)
{
fp=fopen("Remeberdata.txt","a+");//打开增强记忆文件
if(!fp)
{
printf("\n打开文件Remeberdata.txt失败!");
}
i = rand()%num;//在已有词汇中随机选取
printf("\n【%s】请输入英文单词:",s[i].ch);
scanf("%s",b1);
int flag = 0;
for(z=0;strcmp(b1,s[i].en)!=0;z++)
{
if(z<=3){

fprintf(fp,"\n%s %s ",s[i].en,s[i].ch);//回答错误时写入增强记忆文件
printf("\n");
printf("\n输入错误!!请重新输入:");
scanf("%s",b1);c=c+1;

rnum++;}
else{
printf("小伙子,挣扎有用吗,快滚去看字典");
flag=1;
break;
}
}
if(flag)
{return;
}
fclose(fp);
printf("\n恭喜你,回答正确,加10分!\n\n");d=d+1;
printf("\n继续测试请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;

}
}
//英译中测试
void yztest()
{
char b1[20];
int z,x=41;
int choose=1;
int i;
//i = rand()%num;写入循环中
FILE *fp;


while(choose)
{
i = rand()%num;
fp=fopen("Remeberdata.txt","a+");
if(!fp)
{
printf("\n打开文件Remeberdata.txt失败!");//打开增强记忆文件
}
printf("【%s】请输入中文意思:",s[i].en);
scanf("%s",b1);
int flag = 0;
for(z=0;strcmp(b1,s[i].ch)!=0;z++)
{if(z<=3){

fp=fopen("Remeberdata.txt","a+");
if(!fp)
{
printf("\n打开文件Remeberdata.txt失败!");
}
fprintf(fp,"\n%s %s ",s[i].en,s[i].ch);//回答错误时写入增强记忆文件
printf("\n");
printf("输入错误!!请重新输入:");}
else
{printf("小伙子,别挣扎了,看词典吧 ");
flag = 1;
break;}
scanf("%s",b1);c=c+1;
rnum++;
}

if(flag)
{
return ;
}fclose(fp);

printf("\n恭喜你,回答正确,加10分!\n\n");d=d+1;
printf("\n继续测试请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;
}
}
//中英增强记忆
void zyRetest()
{
char b1[20];
int z;
int choose=1;
int i;
int m = 0;
while(choose)
{
i = rand()%rnum;
printf("\n【%s】请输入英文单词:",r[i].ch);
scanf("%s",b1);
for(z=0;strcmp(b1,r[i].en)!=0;z=z)
{
printf("\n输入错误!!请重新输入:");
scanf("%s",b1);c=c+1;
}
printf("\n恭喜你,回答正确,加10分!\n\n");d=d+1;
printf("\n继续测试请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;

}
}
//英中增强记忆
void yzRetest()
{
char b1[20];
int z,x=41;
int choose=1;
int i;
while(choose)
{
i = rand()%num;
printf("【%s】请输入中文意思:",r[i].en);
scanf("%s",b1);
for(z=0;strcmp(b1,r[i].ch)!=0;z=z)
{
printf("输入错误!!请重新输入:");
scanf("%s",b1);c=c+1;
}
printf("\n恭喜你,回答正确,加10分!\n\n");d=d+1;
printf("\n继续测试请选1,返回上一级请选0:");
scanf("%d",&choose);
if(choose==0) return;
}
}
//成绩列表
void list()
{
printf("\n共计输入错误:%d次**每次扣10分**\n",c);
printf("共计输入正确:%d次**每次加10分**\n",d);
printf("你的总得分为:%d分\n\n",10*d-10*c);//计算最后得分
}
//词典维护
int maintain()
{
int choose;//维护功能选择
printf(" ------------------\n");
printf(" 1.增加单词\n");
printf(" 2.修改单词\n");
printf(" 3.删除单词\n");
printf(" 4.查询单词(中)\n");
printf(" 5.查询单词(英)\n");
printf(" 6.退出本菜单\n");
printf(" ------------------\n");

while(1)
{

printf(" \n请输入维护功能编号:");//输入非法排错
scanf("%d",&choose);
int ret;
ret=choose;
while (ret != 1||ret !=2||ret!=3||ret!=4||ret!=5||ret!=6)//将输入限定在0-6的整数
{ if(ret>6||ret<=0){

while (getchar() != '\n');
printf("error input,please again.\n");
scanf("%d",&choose);
ret =choose;
}
else
break;

}//直到输入的值为整数*/
ret=choose;
switch(ret)
{
case 1:
add();writefile();break;
case 2:
modify();writefile();break;
case 3:
del();writefile();break;
case 4:
search();break;
case 5:
ysearch();break;
case 6: return 0;
default: printf("\n请在1-6之间选择");
}
}
}
//用户界面
void menu()
{
int item;
printf(" \n");
printf(" __________________________________________________________\n");
printf(" || ||\n");
printf(" || 英语单词小助手 ||\n");
printf(" || ||\n");
printf(" || 版本 : v1.0 ||\n");
printf(" || ||\n");
printf(" __________________________________________________________\n");
printf(" || ||\n");
printf(" || 0.词库维护 1.单词预览 ||\n");
printf(" || ||\n");
printf(" || 2.单词背诵(中英) 3.单词背诵(英中) ||\n");
printf(" || ||\n");
printf(" || 4.查询成绩 5.帮助 ||\n");
printf(" || ||\n");
printf(" || 6.增强记忆(中英) 7.增强记忆(英中) ||\n");
printf(" || ||\n");
printf(" || 8.退出 ||\n");
printf(" || ||\n");
printf(" __________________________________________________________\n");
printf("\n");
printf(" 请选择您需要的操作序号(0-8)按回车确认:");

scanf("%d",&item);
printf("\n");
int ret;
ret=item;
while (ret != 1||ret !=2||ret!=3||ret!=4||ret!=5||ret!=6||ret!=7||ret!=8)//将输入限定在0-6的整数
{ if(ret>8||ret<0){

while (getchar() != '\n');
printf("error input,please again.\n");
scanf("%d",&item);
ret =item;
}
else
break;

}//直到输入的值为整数*/
ret=item;
readfile();
switch(ret)
{
case 0:
show();maintain();break;//词库维护
case 1:
show();break; //单词预览
case 2:
zytest();break;//中英背词测试
case 3:
yztest(); break;//英中背刺测试
case 4:
list();break;//成绩查询
case 5:
help();break;//使用帮助
case 6:
readRemeberfile();zyRetest();break;//调用文件读入已插入词汇,进行重复中英记忆
case 7:
readRemeberfile();yzRetest();break;// 调用文件读入已插入词汇,进行重复英中记忆
case 8:
select =0;break;//退出子选项
default:
printf("请在0-8之间选择\n");
}
}
int main()
{
while(select)
{
menu();
}
system("pause");
return 0;
}

四. 经验归纳

此次程序设计在原有基础程序的基础上,实现功能的完善,侧重于程序的结构化及精细化编写。在单词汉英查询模块,使用了string库 strcmp函数,利用字符Ascll码的比较实现字符的相同与否判断。在判断输入输入字符是否为纯英文时,用到位运算右移八位得到最高位二进制,1则为汉语,0为英文。

通过本次程序代码编写,再次熟悉了文件操作,认识到程序结构化的重要性。虽然在上课时强调过少用甚至不用goto语句,但是在中英文识别的函数中由于while(for(if(flag)))的嵌套使用,导致逻辑有些混乱,还是不可避免的使用了goto语句,好在最终最终解决了问题。所以在以后设计逻辑算法的时候,要尽量用能驾驭的语句实现,否则会出现意想不到的逻辑错误,甚至到用goto语句破坏程序结构性的地步。

Spring Mvc 跨服务器上传图片405、403、409异常

1.0 status 403

详情如图:
status 403
原因: 在使用local Tomcat 部署项目时,因为Tomcat服务器默认为文件只读模式,所以在调用webResource的put()方法时字节数组无法通过服务器写入对应的url地址写入。

解决方法:更改Tomcat服务器文件读写模式

在本地安装的Tomcat文件夹下找到如下参考路径:
D:\Tomcat9.0_Tomcat9.2\conf\web.xml
在这里插入图片描述

1
2
3
4
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

1.1 status 405

当修改了Tomcat服务器web.xml文件后,只重启了发送端服务器,而图片接收端服务器没有重启时,就会出现以下异常。

status 405

解决方法:将两个服务器都进行重启,重新部署刷新项目

2.0 status 409

修改完Tomcat服务器web.xml,项目重启正常时,如果在接受图片的服务器没有路径中的文件夹时,会出现如下异常。
在这里插入图片描述
原因,调用webResource的put()方法时无法找到图片服务器相应的资源文件夹。因为服务器打包项目后资源文件存在target文件夹下,在其路径下找到与项目名称相同的文件夹新建路径中的目标文件夹即可(以uploads为例),如下图。
在这里插入图片描述

特别注意的是:如果项目采用maven Tomcat插件进行部署的话,则需要在本地maven仓库中找到Tomcat的相应文件进行更改。花费时间较多,如果项目只是练手的话,建议改成本地Tomcat服务器进行部署。

servlet 实现简易验证码

一.验证码本质解释

1.本质:图片
2.目的:防止恶意表单注册

二.验证码生成过程分析

新建JavaWeb 项目,在src 下新建Servlet ,编写servlet逻辑代码
在这里插入图片描述

三.代码实现

新建JavaWeb 项目,在src 下新建Servlet ,编写servlet逻辑代码,在tomcat服务器部署项目

servlet 代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78


import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import javax.imageio.ImageIO;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/checkCodeServlet")
public class checkCodeServlet extends HttpServlet {

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int width=500;
int height=300;
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


//美化图片
//2.1 填充背景色
Graphics g = image.getGraphics();
g.setColor(Color.pink);
g.fillRect(0,0,width,height);

//画边框
g.setColor(Color.red);
g.drawRect(0,0,width-1,height-1);
String str = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890";
//生成随机角标
g.setColor(Color.blue);
Random rand = new Random();
//写验证码
for (int i = 0; i <=4; i++) {
int index= rand.nextInt(str.length());
char ch= str.charAt(index);
g.drawString(ch+"",width/5*i,height/2);
}

//画干扰线
g.setColor(Color.GREEN);
for (int i = 0; i <10 ; i++) {
int x1=rand.nextInt(width);
int x2=rand.nextInt(width);

int y1=rand.nextInt(width);
int y2=rand.nextInt(width);



g.drawLine(x1,y1,x2,y2);
}



//将图片输出到页面
// ImageIO.write(image,"jpg",resp.getOutputStream());
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(resp.getOutputStream());


encoder.encode(image);


}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}

四.实现效果

在这里插入图片描述

部署完成后,刷新浏览器页面,即可实现验证码图片的更新

python 实现图灵机XN_2 模拟

一. 题目分析

通过编程模拟某一图灵机的工作过程,掌握图灵机的概念与基本结构。将图灵机内态变化及指令输出用高级语言实现逐行输出。从而理解图灵机的编码方式。

二. 算法构造

  1. 实现输入十进制整数的二进制编码,并将其转化为二进制扩展码。
  2. 选择图灵机(XN*2),将1转化过来的二进制扩展码用if(elif)语句实现判断后的图灵机指令操作。
  3. Show_binary函数对2中turing_operate操作后的二进制码(字符串)进行输出。
  4. 调用方法,实现完整功能

三. 算法实现

Python(Pycharm)编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
def conversion_nu():
decimalism = int(input("请输入您的整数:"))
binary = bin(decimalism).replace('0b', '') # bin 函数转化十进制
print("转化为二进制为:"+binary)

tensor = list(map(int, binary)) # 将二进制转为列表

if tensor[0] == '0': # 判断输入值0时的情况
return 0

else:
new_tensor = [0, 1, 0] # 任何非零数据转化二进制第一位010
for a in tensor[1:]:
if a == 1:
new_tensor.append(10) # 对第二位后的值进行操作,将操作值存在new_tensor中
else:
if a == 0:
new_tensor.append(0)
new_tensor.append(110)

return new_tensor # 返回值作为turing_operate 的参数


'''turing machine operation
'''


def turing_operate(new_tensor):

tend = [str(i) for i in new_tensor]
tend.append("0") # 内态补位操作
tend.append('0')

a = ''.join(tend)
eve_tend = list(a) # 转化为单值字符

state = '0' # 初始化内态
arr = []
'''
图灵机操作
'''
for i in range(0, len(eve_tend)):
print("第" + str(i+1) + "步")
if state == '0 'and eve_tend[i] == '0':

state = '0'
eve_tend[i] = '0'
elif state == '0' and eve_tend[i] == '1':

state = '1'
eve_tend[i] = '0'
elif state == '1' and eve_tend[i] == '0':

state = '0'
eve_tend[i] = '1'
elif state == '1' and eve_tend[i] == '1':

state = '10'
eve_tend[i] = '0'
elif state == '10' and eve_tend[i] == '0':

state = '11'
eve_tend[i] = '1'
elif state == '11' and eve_tend[i] == '0':

state = '0'
eve_tend[i] = '1'

arr.append(eve_tend)
carry = [str(i) for i in eve_tend] # 聚合操作后的列表
temp = ''.join(carry)
print("当前内态: "+state+" "+"当前扩展码: "+temp)
print("stop")
return arr


def binary_to_metric(arr):
metric = arr[0]
tes = [int(i) for i in metric]
#print(tes)
final = []
for e in range(0, len(tes)):
if tes[e] == 1 and tes[e + 1] == 0:
final.append(1)

elif (e + 1) < len(tes) and tes[e] == 0 and tes[e + 1] == 0:
final.append(0)

elif tes[e] == 1 and tes[e + 1] == 1 and tes[e + 2] == 0:
final.append(',')
break
final.remove(',')
ls = [str(i) for i in final]
ls1 = ''.join(ls)
print('转化完成的二进制码为:' + ls1)
F = int(ls1, 2)
print("XN*2: ", end=" ")
print(F)


"""
调用函数
"""
s = conversion_nu() # 调用
h = turing_operate(s) # 函数作为参数传入
binary_to_metric(h)

四. 调试,测试及运行结果

小数据测试在这里插入图片描述)较大数据测试在这里插入图片描述

五. 经验归纳

本次程序设计,算法相对来说较为简单明了,实现起来不算困难,但因为自学了python,想用python 完成此次编程。由于是第一次用python写具有功能性的代码,遇到了不少困难。首先是函数间参数传入问题,c++及java中都不存在init函数(self)参数的问题,参数传入较好理解,python中此参数让人备受困扰,在查阅相关博客时还学习到python可以将类内函数作为另一函数的参数传入,在Java中只能做到函数间的相互调用。其次是python的对象函数调用,因为没有python的编程经验,此方面花了不少时间,但也学到了面对对象的方法在不同语言中的通用性及区别。

补充:

1.当python出现列表套列表时,可将里边的列表当作是外边的列表元素处理,将里边的列表剥离出来时只需要另新建变量使其等于外边列表的其所处的索引。
2.python函数之间传值时可能会出现值变化的情况,此时一定要注意传参顺序,和传参的数据类型。另外,python可将函数作为参数传入另一个函数,此时就需要注意函数是否被重复调用。

java实现最大公约数最大公倍数求解及Hankson问题

1.java实现最大公约数最大公倍数求解

2.hankson问题求解

一. 题目要求

基本要求: 求N个数的最大公约数和最小公倍数。用C或C++或java或python语言实现程序解决问题。
1.程序风格良好(使用自定义注释模板)
2.提供友好的输入输出,并进行输入数据的正确性验证。
提高要求:
Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x满足:
1、 x和a0的最大公约数是a1;
2、 x和b0的最小公倍数是b1。
Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。
输入格式
输入第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。
输出格式
输出共n行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的x,请输出0;
若存在这样的x,请输出满足条件的x的个数;
样例输入
2
41 1 96 288
95 1 37 1776
样例输出
6
2

二.算法设计思路

1. 多个数据的最大公约数和最小公倍数
根据两个数据的算法,采用迭代法实现多个数据的计算。先计算出前两个数的最大公约数及最小公倍数,递归代入先前定义的计算函数。从而达到计算N个的目的。
2 . hankson.通过数学推算x的取值在a0~b1间,用循环代入题目要求,达到计算x 个数的目的。

三. 代码实现(java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

import java.util.Scanner;
/*
* 1.实现多整数最大公约数,最大公倍数求解
* 2.实现hankson问题x个数的计数*/
public class Ngdc {
static int gcds (int a,int b)//递归调用//两个数的最大公约数求解

{
if(a%b==0)
return b;
else
return gcds(b,a%b);
}
static int multiple(int x,int y)//两个数的最小公倍数求解
{
return(x*y/gcds(x,y));
}
private static int ngcd(int []a)//n个数的最大公约数求解
{
int i=0;
int temp=gcds(a[i],a[i+1]);
i+=2;
while(i<a.length)
{
gcds(temp,a[i]);
i++;
}
return temp;
}
private static int nlcm(int []a)//n个数的最小公倍数求解
{
int i=0;
int temp=multiple(a[i],a[i+1]);
i+=2;
while(i<a.length)
{
multiple(temp,a[i]);
i++;
}
return temp;
}
private static int[][] setArray()//放置数据 函数
{
Scanner sc = new Scanner(System.in);
System.out.println("请输入您想输入数据的组数:");
int n=sc.nextInt();
if(n<1)
{System.out.println("请重新输入");}

int[][] array = new int[n][4];
for(int i = 0; i < array.length; i++) {
for(int j = 0; j < 4; j++) {
array[i][j] = sc.nextInt();
}

}
sc.close();
return array;

}
private static int judgecount(int array[][])//x个数计算函数
{
int count=0;
for(int e=0;e<array.length;e++) {
if(array[e][0]%array[e][1]!=0&&array[e][3]%array[e][2]!=0)

{System.out.println("输入非法,重新输入 (使每一行第一个数被第二个整除,第四个被第二个整除)"); }
else {
for(int i=array[e][0];i<=array[e][3];i++) {
if(gcds(i,array[e][0])==array[e][1]&&multiple(i,array[e][2])==array[e][3])
{count++;
}


}
System.out.println("x个数 "+count*2);}}
return 0;
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请选择您想要的操作方式:1.N个数的最大公约数 2.Hankson");
int s=sc.nextInt();
switch (s) {
case 1:
System.out.println("请输入您想输入的数据个数N:");
int n=sc.nextInt();
int[] array=new int[n];
if(n<2)
{System.out.println("输入有误,请输入大于2的整数");
}else
for(int i=0;i<n;i++)
{
array[i]=sc.nextInt();
}
System.out.println("这N个数的最大公约数是:"+ngcd(array));
System.out.println("这N个数的最小公倍数是:"+nlcm(array));
sc.close();
break;
case 2:
int arr[][]=setArray();//接收setArray()返回值
judgecount(arr);//调用 x 计数函数
}
}}

四.测试截图

在这里插入图片描述
在这里插入图片描述

五.个人总结

此次程序设计基本要求算法较为简单,在上次的算法基础上变通,复用即可实现。所以差距现在代码的简洁和高效性上。提高要求中Hankson 算法将题目要求变成代码需要进行对X 的变通。经过数学归纳得出X 值所在范围后,问题得到简化。
虽然本学期开了java课,但用java写一个正真具有功能的代码还是第一次,本次程序设计进一步熟悉了java语法及函数之间的调用,也认识到了java语言的强大。不足之处很多,如在编码的时候经常遇到语法及算法问题,编程实践能力欠缺,时间也拖得有些长。另外,在数据的异常处理方面,也做得不够好,想用try catch 语句,但是由于还没有掌握,便放弃了。只进行了简单的异常处理,达不到完全符合要求。程序的模块化也做得不够好,争取在接下来的时间里填补空缺,下次做得更好。

Maven 创建webapp骨架无法使用@WebServlet注解配置

利用maven框架创建web工程

创建 Maven  web 工程
如上图所示,在利用maven骨架创建web项目时,默认选择创建webapp:1.4版本,由于在早期的web工程中并不支持@WebServlet注解配置,甚至不支持El表达式(在web 3.0版本之后才支持),所以在写servlet 时想使用@WebServlet进行url-pattern配置是不能够完成的。

问题解决

1.改变web.xml文件头来更改web工程版本

  • 找到本地仓库文件 如 :E:\maven_repository
  • 打开创建web工程的依赖jar包 路径如下:E:\maven_repository\org\apache\maven\archetypes\maven-archetype-webapp\1.4
  • 在这里插入图片描述
  • 如图用压缩软件打开jar包

    注意:不是解压缩

    修改web.xml文件
    修改代码如下
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="false">
</web-app>

注意一定要将matadata-complete属性改为”false”这样创建的web工程才是web-4.0

  • 此处代码只是一个参考,正确做法是打开TomCat安装路径找到以下文件复制粘贴到上述的jar包web.xml中
  • Tomcat 中路径如下:D:\Tomcat9.0_Tomcat9.2\webapps\ROOT\WEB-INF
  • 修改参数
    一定注意要修改metadata-complete参数(重要的话说两遍)
    这样就完成了webapp项目版本的升级

2.创建web-4.0工程后实现@WebServlet注解配置

要实现maven 工程的Servlet编写,首先要在pom.xml中使用来导入依赖,代码如下:

servlet -api 依赖有代码中所示的两种版本,已注释的版本不支持@WebServlet注释配解 请务必使用下边未加注释的版本,另外注意在 3.1.0后加上 provided标签,不加此标签也会导致注解配置不能使用。

1
2
3
4
5
6
7
8
9
10
11
<!--    <dependency>-->
<!-- <groupId>javax.servlet</groupId>-->
<!-- <artifactId>servlet-api</artifactId>-->
<!-- <version>2.5</version>-->
<!-- </dependency>-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

进行上述更改后maven创建的web项目在可正常使用@WebServlet进行注释配解。

Linux文件目录及常用命令

1.1文件和目录简介

Linux和windows 文件存储方式不同,Windows有多个根目录(/c/d/e/f)linux 只有一个根目录
Linux 根目录: 文件–》 其他–》计算机

根目录:

  1. /bin 二进制文件
  2. /home 用户目录
  3. /home/xxx(user) 用户家目录
  4. /etc 系统配置文件目录
  5. /root 超级管理员的目录

1.2 Linux终端命令格式

终端:软件控制台,在终端输入命令可快速操作计算机
终端命令格式: 命令 [-选项] [-参数]

查看帮助的两种方式

(1).man 命令
【enter】 换行
【空格】 翻页
f - 下一页
b - 上一页
q - 退出
(2).help
命令 –help

1.3 常用命令— 显示文件及目录

*pwd 显示当前所在的路径
*tree 以树状列表方式显示文件夹内容
(1)tree 当前目录
(2)tree aaa[指定目录]
*ls 一列表方式显示 文件夹内容
ls -a 显示所有文件(包含隐藏文件)

1.3.1显示文件详细信息

ls两个选项
-l 以详细信息的方式显示列表内容
-h 以更加人性化的方式显示文件夹内容(文件大小 k MB..)

1.3.2 切换目录

(1).在图形界面进入该文件目录,鼠标右击显示复选框,选择在终端打开即可进入该目录下
在这里插入图片描述
(2)cd 命令,切换工作目录
用法:
cd 路径 切换到指定路径
cd 回到家目录
cd ~ 回到家目录
cd . 当前目录
cd .. 上级目录
cd - 上次目录

1.4 常用命令—创建文件和文件夹

  • mkdir 创建目录 ,递归创建 添加 -p 选项
  • touch 创建一个文件:touch 文件名
  • touch 创建多个文件:touch filename1 filename2
  • gedit 用来打开一个文件并进行编辑
    (1).打开一个文件后,终端进入等待状态
    (2).可以同时编辑多个文件 gedit filename1 filename2
1.4.1 常用命令- 删除文件和目录
  • rm ->remove
    删除文件或者目录
    删除文件: rm filename

此命令提供两种选项
-i 以交互模式删除 (提示是否确认删除)
-f 强制删除不提示
删除文件夹 rm -r filename 递归删除目录中的内容

1.4.2 常用命令-文件拷贝

cp ->copy
用来拷贝文件或目录
拷贝文件 : cp 原路径 目标路径
如: cp ./Javascript/demo.txt ./java/demo2.txt
该命令提供如下选项
-i 交互模式拷贝
-f 强制覆盖不提示
-v 显示拷贝过程
-a 拷贝原有属性

·拷贝目录 cp -r 源路径 目标路径

1.4.3 常用命令-移动

mv ->move
1.移动文件或文件夹
mv 源路径 目标路径
选项可选如下:
-i 交互方式进行文件移动
-f 强制覆盖不提示
-v 显示移动过程
注意:移动文件夹不需要加 -r 选项
2.重命名文件及文件夹
重命名: 在一个目录下移动才能进行重命名
mv 旧文件名 新文件名

1.5 常用命令-其他

1. clear
清除当前终端显示内容 如果你想秀操作的话 使用快捷键 【ctrl】+【l】
2. 【Tab】
自动补全文件夹或者文件名
注意:当在当前文件夹下有相同开头文件名时,补全功能无法实现,此时按两下Tab 系统显示当前文件夹下的所有以所写命令开始的文件名
3. which
查看某个命令所在的位置
如: which gedit
4. 终止指令的执行
【ctrl】+【c】
5.终端字体的大小调整
放大: ctrl +shift+=
缩小: ctrl+ -

1.6 常用命令-日历日期

cal 查看日历

cal -3 查看上月,当前月,下一月
cal -y 显示当年的日历
cal -j 以一年中第—天的形式显示日历
cal + 具体年份 显示所写年份日历

date 查看日期时间

date 显示当前的年月日时分秒

格式化显示

date ”+%Y” 输出年份
date ”+%m” 输出月份
date ”+%d” 输出天
date ”+%H” 输出时
date ”+%M” 输出分
date ”+%S” 输出秒
date ”+%F” 格式化输出 如2019-7-19
date ”+%T” 格式化输出 如19:56:21

1.7 常用命令-查看历史指令

  1. history 查看当前用户服务器所有历史指令

  2. history 数量 显示一定数量的历史指令

  3. !历史指令编号 执行该条历史指令

    历史指令保存在~/.bash_history 文件中

1.8常用命令-文件查看(cat ,more)

·cat 查看文件或连接文件

| 1.查看文件
cat 文件名
选项:
-n 查看文件时,对每一行进行编号
-b 非空行进行编号
-s 连续两行以上的空行,只显示一行
| 2.连接文件,把两个文件合并到 一起进行输出
cat filename1 filename2

·more 用来分屏查看文件内容

选项:
+num 从第num行开始查看文件 如:more ./demo.txt +20
-p 先清屏,再查看文件
-s 连续两行以上的空行,只显示一行
快捷键:
【enter】查看下一行
【空格】查看下一屏
Ctrl + f 查看下一屏
Ctrl + b 查看上一屏
q 退出

1.9 常用命令-数据流 、管道

在这里插入图片描述
·数据流

  • 输入流
  • 输出流
  • 标准的错误输入输出流

·重定向:改变数据流向(一般定向到文件中)

  1. 重定向

  2. 以追加的方式重定向

·管道

  • | 指令1 |指令2 ps:指令1必须要有输出

如: touch demo.txt |more 因为指令1无输出,所以该管道使用错误

  • © 2019-2020 卻水
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信