学号 2019-2020-1823 《数据结构与面向对象程序设计》实验六报告
班级: 1823
姓名: 杨凯涵
学号:20182321
实验教师:王志强
实验日期:2019年10月22日
必修/选修: 必修
1.实验内容
1.链表练习,要求实现下列功能:
通过键盘输入一些整数,建立一个链表;
这些数是你学号中依次取出的两位数。 再加上今天的时间。
例如你的学号是 20172301
今天时间是 2018/10/1, 16:23:49秒
数字就是
20, 17,23,1, 20, 18,10,1,16,23,49
打印所有链表元素, 并输出元素的总数。
在你的程序中,请用一个特殊变量名来纪录元素的总数,变量名就是你的名字。 例如你叫 张三, 那么这个变量名就是
int nZhangSan = 0; //初始化为 0.
做完这一步,把你的程序签入源代码控制(git push)。
2.链表练习,要求实现下列功能:
实现节点插入、删除、输出操作;
继续你上一个程序, 扩展它的功能,每做完一个新功能,或者写了超过10行新代码,就签入代码,提交到源代码服务器;
从磁盘读取一个文件, 这个文件有两个数字。
从文件中读入数字1, 插入到链表第 5 位,并打印所有数字,和元素的总数。 保留这个链表,继续下面的操作。
从文件中读入数字2, 插入到链表第 0 位,并打印所有数字,和元素的总数。 保留这个链表,并继续下面的操作。
从链表中删除刚才的数字1. 并打印所有数字和元素的总数。
3.链表练习,要求实现下列功能:
使用冒泡排序法或者选择排序法根据数值大小对链表进行排序;
如果你学号是单数, 选择冒泡排序, 否则选择选择排序。
在排序的每一个轮次中, 打印元素的总数,和目前链表的所有元素。
在(2)得到的程序中继续扩展, 用同一个程序文件,写不同的函数来实现这个功能。 仍然用 nZhangSan (你的名字)来表示元素的总数。
4.在android上实现实验(1)和(2)
5.在android平台上实现实验(3)
2实验过程及结果
实验一
实验一代码如下
File file = new File("D:\\idea\\file", "test.txt"); Reader reader2 = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(reader2); Scanner scan = new Scanner(System.in); System.out.println("请输入一组数,并用空格号隔开 "); String s = ""; s = scan.nextLine(); String[] a = s.split("\\s"); Linearlist<Integer> list = new Linearlist<Integer>(); for (int i = 0; i < a.length; i++) { int num = Integer.parseInt(a[i]); list.add(num); } int yangkaihan = 0; yangkaihan = list.isEmpty(); System.out.println(list.toString()); System.out.println("元素的总数是"+yangkaihan);
前面一段是实验二的代码,后面一段是实验一的代码。我们字符串读取一整行的输入数字,接着,以空格号为分割线把每个数字给隔开,单独成为一个数组,然后一个接着一个输入进我们的链表里,最后再用链表的toString方法打印出来即可。
实验二
实验二的代码如下
File file = new File("D:\\idea\\file", "test.txt"); Reader reader2 = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(reader2); Scanner scan = new Scanner(System.in); System.out.println("请输入一组数,并用空格号隔开 "); String s = ""; s = scan.nextLine(); String[] a = s.split("\\s"); Linearlist<Integer> list = new Linearlist<Integer>(); for (int i = 0; i < a.length; i++) { int num = Integer.parseInt(a[i]); list.add(num); } int yangkaihan = 0; yangkaihan = list.isEmpty(); System.out.println(list.toString()); System.out.println("元素的总数是"+yangkaihan); String line=""; line = bufferedReader.readLine(); String[] c =line.split("\\s"); int e=Integer.parseInt(c[0]); int b=Integer.parseInt(c[1]); list.insert(5,e); System.out.println("从文件中读入数字1(第一个数字), 插入到链表第 5 位"+list.toString()+" 元素总数为"+list.isEmpty()); list.insert(1,b); System.out.println("从文件中读入数字2(第二个数字), 插入到链表第 0 位"+list.toString()+" 元素总数为"+list.isEmpty()); list.delete(5); System.out.println("删除了数字1后"+list.toString());
添加
LinearNode<T> c = new LinearNode<>(elemt); temp.setNext(c); temp=c;
插入
LinearNode<T> top = new LinearNode<>(); temp = head; for(int i=1;i<=a;i++) { top = temp; temp = temp.getNext(); } LinearNode<T> c = new LinearNode<T>(b); top.setNext(c); c.setNext(temp); temp=top;
删除
temp = head; for(int i=1;i<a;i++) { temp=temp.getNext(); } LinearNode<T> c = new LinearNode<T>(); c=temp; temp=temp.getNext(); temp=temp.getNext(); c.setNext(temp); temp=c;
读取文件的内容是上一章的内容,在此就不做过多的议论,我们在提取出一个字符串后,经过拆分、转换觉得处理,得到两个整数,整数1和整数2,接着利用插入函数,把它插入到指定的位置,先是得到所要插入位置的节点和此节点的前一个节点,接着再将前一个节点如下图(画的有点丑)
而delete一个节点的话,就是直接讲要删除的节点的前一个节点直接连接到它的下一个节点就好了(即跨过他,忽略掉它)大致如下图
这样就可以得到正确的结果了
实验三
因为我的学号是单数,所以我做的冒泡排序,冒泡排序的思路设计是这样的,首先,我们先看c语言里的冒泡排序
/* 冒泡排序 */ /* 1. 从当前元素起,向后依次比较每一对相邻元素,若逆序则交换 */ /* 2. 对所有元素均重复以上步骤,直至最后一个元素 */ /* elemType arr[]: 排序目标数组; int len: 元素个数 */ void bubbleSort (elemType arr[], int len) { elemType temp; int i, j; for (i=0; i<len-1; i++) /* 外循环为排序趟数,len个数进行len-1趟 */ for (j=0; j<len-1-i; j++) { /* 内循环为每趟比较的次数,第i趟比较len-i次 */ if (arr[j] > arr[j+1]) { /* 相邻元素比较,若逆序则交换(升序为左大于右,降序反之) */ temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } int main (void) { elemType arr[ARR_LEN] = {3,5,1,-7,4,9,-6,8,10,4}; int len = 10; int i; bubbleSort (arr, len); for (i=0; i<len; i++) printf ("%d\t", arr[i]); putchar ('\n'); return 0; }
这是c语言里的冒泡排序,即从头到尾两个元素两两不停比较,这样就会让最小(最大)的数一个接着一个的向左边移动,就像是水池里冒出来的泡泡一般。
c语言里使用的是数组,而我们用的是链表,那么数组和链表的区别是什么呢?没错,链表无法像数组一样“精准打击”,数组可以从第一个元素直接跳到第四第五个元素,而链表只能一个一个的next下去,但是这似乎并不影响我们的冒泡排序,那如何根据这个数组的冒泡排序来写出链表的冒泡排序呢?————其实就是当数组的下标i++时候,我们的链表next一个就好了
temp = head.getNext(); LinearNode<T> c; LinearNode<T> d; c=head.getNext(); d=head.getNext(); for(int i = 0;i<count-1;i++) { c=temp; d=c.getNext(); for(int j =0;j<count-i-1;j++) { if(((int)c.getElement())<((int)d.getElement())) { T t; t = c.getElement(); c.setElement(d.getElement()); d.setElement(t); } System.out.println("\n"+"冒泡排序第"+i+"轮结果"+this.toString()); c=c.getNext(); d=d.getNext(); } }
以上是我的代码,定义了三个链表的指针,其实就是一个是a[i],其他两个是a[j]和a[j+1],按照这c语言里的思路,当c里面的数组往下走的时候,我们的链表也跟着往下走,而循环里的i、j不变。照着这个思路走,把这个代码写完。效果如图
实验四
本次安卓其实代码难度并没有很难,就是设置一个监听器就好了,接着把代码打出去便可,唯一不同的是安卓的读取文件有些许的不同,这里在后面的问题会给解释一下,以下是我的监听器的代码
AutoCompleteTextView s; AutoCompleteTextView s2; AutoCompleteTextView s3; AutoCompleteTextView s4; AutoCompleteTextView s5; AutoCompleteTextView s6; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); s = findViewById(R.id.autoCompleteTextView); s2 = findViewById(R.id.autoCompleteTextView2); s3=findViewById(R.id.autoCompleteTextView3); s4=findViewById(R.id.autoCompleteTextView4); s5=findViewById(R.id.autoCompleteTextView6); s6=findViewById(R.id.autoCompleteTextView7); Button cal = findViewById(R.id.button); cal.setOnClickListener(onClickListener); } View.OnClickListener onClickListener = new View.OnClickListener() { Linearlist<Integer> list = new Linearlist<Integer>(); public void onClick(View v) { String get = s.getText().toString(); String[] a1 = get.split("\\s"); for(int i=0;i<a1.length;i++) { int num = Integer.parseInt(a1[i]); list.add(num); } s3.setText("链表元素为"+list.toString()); InputStream input=getResources().openRawResource(R.raw.test); /* Reader reader=new InputStreamReader(input);*/ BufferedInputStream bufferedInputStream = new BufferedInputStream(input); byte[] buffer = new byte[1024]; String content = ""; int flag = 0; while (true){ try { if (!((flag =bufferedInputStream.read(buffer))!=-1)) break; } catch (IOException e) { e.printStackTrace(); } content += new String(buffer,0,flag); } /* StringBuffer stringBuffer=new StringBuffer(); char b[]=new char[1024]; int len=-1; try { while ((len = reader.read(b))!= -1){ stringBuffer.append(b); } }catch (IOException e){ Log.e("ReadingFile","IOException"); }*/ String string=content; String[] a =string.split("\\s"); int num1 = Integer.parseInt(a[0]); int num2 = Integer.parseInt(a[1]); list.insert(5,num1); s2.setText("数字1(7)插入5位"+list.toString()); list.insert(1,num2); s4.setText("数字2(10)插入0位"+list.toString()); list.delete(5); s6.setText("删除了数字1(7)后"+list.toString()); int count = list.isEmpty(); list.maopao(count); /* s5.setText("排序后为"+list.toString());*/ } };
如上图就是设置了一个监听器在button按钮这里,当事件发起后,就会进行代码的执行,接着我们就可以输出结果了(当然,在这之前必须要先布局),输出的结果就是在我们之前设置的那些下划线里,这里我设置的是AutoCompleteTextView,每个都设置一个对象,然后找到其相对于的注册点,再对其进行输出就好了。
实验五
实验四的代码中已经有了我实验五的代码了,原理和实验四一样,运行结果如下
3. 实验过程中遇到的问题和解决过程
问题1:
(两次输出连在一起)问题1解决方法:这是没有清空toString中的result而导致的,使得之前的链表没有被清空,带着一起被一起输出出来了,所以我们的解决方法是在toString里定义result,而不是在外围定义result
public String toString() { LinearNode<T> current = new LinearNode<T>(); current = head.getNext(); String result=""; while (current != null) { result +=" "+current.getElement(); current=current.getNext(); } return result; }
问题2:无法分开字符串中的数字
问题2解决方法:分开Java字符串的方法是s.split(“”)双引号内输入的是以什么作为分割,我们想以空格进行分割,所以一开始输入的是
String s = ""; s = scan.nextLine(); String[] a = s.split(" "); Linearlist<Integer> list = new Linearlist<Integer>(); for (int i = 0; i < a.length; i++) { int num = Integer.parseInt(a[i]); list.add(num); }
但是结果是运行错误,经过研究,发现,这是转义字符的问题,正确代码应该是
String s = ""; s = scan.nextLine(); String[] a = s.split("\\s"); Linearlist<Integer> list = new Linearlist<Integer>(); for (int i = 0; i < a.length; i++) { int num = Integer.parseInt(a[i]); list.add(num); }
空格在这里是“\s”而不是“”,因为对某些特殊字符,如果字符(串)正好是正则的一部分,则需要转义才能使用,
这些字符有 | , + , * , ^ , $ , / , | , [ , ] , ( , ) , - , . , \等, 因它们是正则表达式中的一部分, 所以如果想用该字符本身, 这些字符需要进行转义才能表示它本身。
问题3:如何在Android studio里读取文件?
问题3解决方法:首先,安卓读取的文件并不是电脑里的文件,这个必须要分清楚,这也是为什么我一开始失败的原因,网上关于读取文件的方法有很多,我就学了其中的一种,首先,先要建一个文本文件,我们就建在resource文件夹里的raw里
接着,就是就是打入下面的代码
InputStream input=getResources().openRawResource(R.raw.test); /* Reader reader=new InputStreamReader(input);*/ BufferedInputStream bufferedInputStream = new BufferedInputStream(input); byte[] buffer = new byte[1024]; String content = ""; int flag = 0; while (true){ try { if (!((flag =bufferedInputStream.read(buffer))!=-1)) break; } catch (IOException e) { e.printStackTrace(); } content += new String(buffer,0,flag); }
其中
InputStream input=getResources().openRawResource(R.raw.test); /* Reader reader=new InputStreamReader(input);*/ BufferedInputStream bufferedInputStream = new BufferedInputStream(input);
就是为了获得文本的输出流,读取出文本的内容,熟悉这一步之后,我们就可以按照原来的操作处理输入的数字,接着再把字符串setText打印出去。
- 问题4:关于布局
- 问题4解决方法:布局还未有深入的学习与了解,目前知道的比较低级的有效的方法就是在layout文件夹里的文件的design里来设计我们的布局,
同时也要注意,不同的手机所使用的界面的规格不同,所以在design里一定要选好适合自己的手机,接着再用“魔法棒”来帮助我们来设计,这样会更有效。而细微之处的话可以在Text里进行更改。
其他(感悟、思考等)
- 当我们遇到循环方面的问题时候,往往最有效的方法就是debug,通过debug便于我们找出循环里出现的逻辑问题
安卓里实现某种效果,其实更像是一种“组合”,当我们在idea里完成了核心代码的任务后,搬运到安卓里,安卓对其进行包装,最后制作出一个app,主要也只是布局,计算,输入,输出这几个步骤,并没有算法上的问题,更偏向的是一种工程类的模型。
参考资料
《Java程序设计与数据结构教程(第二版)》
《Java程序设计与数据结构教程(第二版)》学习指导