20172325 2018-2019-2 《Java程序设计》第五周学习总结
20172325 2018-2019-2 《Java程序设计》第五周学习总结
教材学习内容总结
本次学习第九章内容,主要学习查找和排序。
查找
-
查找的定义:是一个过程,即在某个项目组中寻找某一项指定目标元素,或者确定该指定目标并不存在。对其进行查找的项目组有时也称为查找池。高效的查找会使该过程所做的比较操作次数最小化。为了尽可能高效的完成查找,从算法分析的角度而言,我们希望最小化比较操作的次数,通常,查找池里项目数目越多,为了寻找该目标而做出的比较操作次数就越多,因此该查找池中项目的树目定义了该问题的大小。
-
查找的分类:线性查找,二分查找。
-
静态方法:静态方法(类方法)可以通过类名调用,不能引用实例变量,可以引用静态变量。Java程序的main方法必须用static修饰符(声明为静态)来修饰,解释器不用实例化含有main的类的对象,就可以调用main方法。
-
泛型方法:泛型方法创建一个引用泛型的方法,只需在方法头的返回类型前插入一个泛型声明即可。含有返回类型和参数类型的方法,就可以使用泛型参数。泛型声明必须位于返回类型之前,这样泛型才可以作为返回类型的一部分。
-
线性查找:
原理:对于一个某类型的列表,从列表头开始依次比较每一个值,直至找到该元素目标或者到达列表尾得出该组中不存在该目标的结论。
特点:方式简单易理解,对数组的要求低,要求数组长度低。
图解: -
二分查找:
原理:二分查找是从排序列表的中间开始查找,而不是从一端或另一端开始。如果没有在那个中间元素找到目标元素则继续查找。
特点:二分查找将利用了查找池是已排序的这一事实;二分查找的每次比较都会删除一半的可行候选项;结合了递归的思想。
图解: -
查找算法比较:
1、对于线性查找和二分查找,最好的情形都是目标元素恰好是我们考察的第一个元素,最坏的情形也都是目标不在该组中。因此,线性查找的时间复杂度为O(n),二分查找的时间复杂度为O(log2(n))。
2、当n比较大时,即元素特别多的时候,二分查找就会大大提高效率。而当n比较小的时候,线性查找更简单好调试且不需要排序,因此也在小型问题上常用线性查找。
排序
-
定义:是一个过程,基于某一标准,要么以升序要么以降序将某一组项目按照某个规定顺序排列。
-
排序的分类(基于效率):顺序排序和对数排序。
-
顺序排序的方式:选择排序、插入排序、冒泡排序。
-
对数排序的方式:快速排序、归并排序。
-
选择排序:
定义:通过反复地将某一特定值放到它在列表中的最终已排序位置,从而完成对某一列值的排序。
图解: -
插入排序:
定义:通过反复地将某一特定值插入到该列某个已排序的子集中来完成对列表值的排序。
图解 -
冒泡排序:
定义:通过重复地比较相邻元素且在必要时将它们互换,从而完成对某个列表的排序。
图解: -
快速排序:
定义:通过将列表分区,然后对这两个分区进行递归式排序,从而完成对整个列表的排序。
图解: -
归并排序:
定义:通过将列表递归式分为两半直至每一字列表都含有一个元素,然后将这些字列表归并到一个排序顺序中,从而完成对列表的排序。
图解:
本章关键代码展示
//线性法查找
boolean linearSearch(T[] data, int min, int max, T target)
{
int index = min;
boolean found = false;
while (!found && index <= max)
{
found = data[index].equals(target);
index++;
}
return found;
}
//二分法查找
public static <T extends Comparable<? super T>>
boolean binarySearch(T[] data, int min, int max, T target)
{
boolean found = false;
int midpoint = (min + max) / 2; // determine the midpoint
if (data[midpoint].compareTo(target) == 0)
found = true;
else if (data[midpoint].compareTo(target) > 0)
{
if (min <= midpoint - 1)
found = binarySearch(data, min, midpoint - 1, target);
}
else if (midpoint + 1 <= max)
found = binarySearch(data, midpoint + 1, max, target);
return found;
}
//选择排序
public static <T extends Comparable<T>>
void selectionSort(T[] data)
{
int min;
T temp;
for (int index = 0; index < data.length-1; index++)
{
min = index;
for (int scan = index+1; scan < data.length; scan++)
if (data[scan].compareTo(data[min])<0)
min = scan;
swap(data, min, index);
}
}
//插入排序
public static <T extends Comparable<T>>
void insertionSort(T[] data)
{
for (int index = 1; index < data.length; index++)
{
T key = data[index];
int position = index;
// shift larger values to the right
while (position > 0 && data[position-1].compareTo(key) > 0)
{
data[position] = data[position-1];//把数值大的数换到了右边
position--;//position变成左边数的索引
}
data[position] = key;
}
}
//冒泡排序
public static <T extends Comparable<T>>
void bubbleSort(T[] data)
{
int position, scan;
T temp;
for (position = data.length - 1; position >= 0; position--)
{
for (scan = 0; scan <= position - 1; scan++)
{
if (data[scan].compareTo(data[scan+1]) > 0)
swap(data, scan, scan + 1);
}//第一个循环里面是第一次两个两个比较到列表末尾
}//第二个循环里面是一共进行内循环的次数,其次数与列表元素个数-1相等
}
教材学习中的问题和解决过程
教材学习有问题先去https://shimo.im/doc/1i1gldfsojIFH8Ip/看看,如果别人没有提出相同问题,可以编辑文档添加,然后把自己提出的问题复制到下面:
-
问题1:在学习快速排序的时候不是很懂排序的原理,没有理解清楚。
-
问题1解决方案:一次循环:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值来说,左右两边就是有序的了。
接着分别比较左右两边的序列,重复上述的循环。
尤其是图片里面的例子,很好的帮助我理解了快速排序法。 -
问题2:<T extends Comparable<? super T>>是什么意思?
-
问题2解决方案:T表示任意字符名,extends对泛型上限进行了限制即T必须是Comparable<? super T>的子类,
然后<? super T>表示Comparable<>中的类型下限为T! -
问题3:泛型的上限下限是什么东西?
在写程序的时候,我们希望某一个函数接收的参数,有一个范围的限制,这时候我们就可以考虑使用泛型的上限和下限去解决。我们先看一段代码,然后再去详细解释。
class Point<T>{
private T x;
private T y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y)
{
this.y = y;
}
}
public class Demo2{
// static void show(Point<? extends Number> p)
//此时这个函数只能接收Number及其子类 此时show(p2)会报错
static void show(Point<? super String> p)//此时这个函数只能接收String及其父类 此时show(p1)会报错
{
System.out.println(p.getX());
System.out.println(p.getY());
}
public static void main(String[] args)
{
Point<Integer> p1 = new Point<Integer>();
p1.setX(2);
p1.setY(4);
Point<String> p2 = new Point<String>();
p2.setX("东经43度");
p2.setY("北纬34度");
show(p1);
show(p2);
}
}
在这段程序中我们定义了一个函数show,我们在参数的后面使用的<? .....>这里的问号,表示的通配符,代表接收的实际参数的类型。我们可以看出来在程序中我先注释了entends的一行代码。这个表示参数的上限,接收的参数必须是Number或者是其子类,如果使用这个的时候,我们调用show方法的时候,传递p2会报错,因为p2不是Number或者其子类。同样的,我们使用super关键字,这个表示参数的上限,程序中表示接收的参数只能是String或者其父类,这时候show方法的参数是p1会报错,不满足下限的要求。
原文
代码调试中的问题和解决过程
-
问题1:
-
问题1解决方案:for循环的条件必须满足在间隔固定数后的也比position小,所以就会产生出界,需要添加条件scan + i <= position; scan++),但由于i是大于1的,所以满足scan + i <= position,一定有scan <= position,所以改为for (scan = 0; scan + i <= position; scan++)
-
问题2:如何计算代码运行时间?
-
问题2解决方案:
第一种(以毫秒为单位):
long startTime = System.currentTimeMillis(); //获取开始时间
doSomething(); //测试的代码段
long endTime = System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
第二种(以纳秒为单位):
long startTime=System.nanoTime(); //获取开始时间
doSomeThing(); //测试的代码段
long endTime=System.nanoTime(); //获取结束时间
System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
代码托管
(statistics.sh脚本的运行结果截图)
上周考试错题总结
满分,无错题。
结对及互评
-
博客中值得学习的或问题:
- 问题记录详细
- 教材总结认真
-
代码中值得学习的或问题:
本次代码问题较少,没有特别需要点评的地方。 -
基于评分标准,我给本博客打分:8分。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 20/20 | |
第二周 | 941/8481 | 1/2 | 18/20 | |
第三周 | 653/9134 | 1/3 | 22/20 | |
第四周 | 1728/4385 | 2/5 | 8/28 | |
第五周 | 933/5318 | 1/6 | 18/20 |