Bootstrap

XJTUSE-数据结构-homework2

当时写的还挺痛苦的

不过现在看,原老师布置的作业真的有水平

现在来看大二数据结构的作业,真的很锻炼代码能力。有些题目,我现在写也不一定能很快写出来hhhh

当时写的作业感觉还是存在问题的!

任务概述

 

 

任务 1 :指定的 List ADT 实现

题目:见pdf

1、使用顺序数组作为存储表示:

数据设计:

  1. private int MAXLEN;  
  2. private char[] seqList = null;  
  3. private int cursor = -1;  
  4. private int tail = 0;

在使用顺序数组作为存储表示中,设计了以上几个私有变量,下面一一进行说明。

MAXLEN表示顺序数组的最大储存空间,这是因为顺序数组是依靠数组的存储空间实现的,必须在一开始规定好其数组的大小。

seqList 表示顺序数组,存储字符,初始化为null。

cursor  表示顺序数组的起始下标,初始化为-1。

tail   表示顺序数组的结尾下标,初始化为0。

算法设计:

  1. public void insert(char newElement) {  
  2.         //  after the cursor  
  3.         if (tail>=MAXLEN){  
  4.             return;  
  5.         }  
  6. //        空数组插入  
  7.         if (tail==0){  
  8.             cursor++;  
  9.             seqList[cursor] = newElement;  
  10.             tail++;  
  11.             return;  
  12.         }  
  13. //        非空插入  
  14.         for (int i=tail-1;i>cursor;i--){  
  15.             seqList[i+1] = seqList[i];  
  16.         }  
  17.         cursor++;  
  18.         seqList[cursor] = newElement;  
  19.         tail++;  
  20.     } 

根据题目中的插入要求,分成了三种基本情况:满数组,空数组,正常插入(非空数组)。根据要求可知道,要先判断满数组和空数组,若都不是,再进行正常插入。因为是顺序数组,所以说进行插入操作需要复制插入位置之后的所有元素,又因为剩下的操作均在常数项时间内能完成,由此插入操作的时间复杂度为O(n)   。

  1. public void remove() {  
  2.         tail--;  
  3.         //        空数组  
  4.         if (tail <= 0){  
  5.             tail = 0;  
  6.             cursor = -1;  
  7.             seqList = new char[MAXLEN];  
  8.             return;  
  9.         }  
  10.         for(int i=cursor;i<tail;i++){  
  11.             seqList[i] = seqList[i+1];  
  12.         }  
  13.         cursor = cursor%tail;  
  14.     }  

删除操作也分为两种基本情况:空数组,正常删除。在删除操作中,要把需要删除的元素后面的元素往前移一位,其它操作均为常数项,因此时间复杂度为O(n)   。需要注意的是,在删除操作中cursor = cursor%tail

很重要,这是因为当删除的元素位于数组的tail位置时,cursor需要指向首元素,因此需要上述操作。

  1. public void replace(char newElement) {  
  2.         if (cursor < 0){  
  3.             return;  
  4.         }  
  5.         seqList[cursor] = newElement;  
  6.     }  

替换操作比较简单,不进行说明,唯一需要注意的是当空数组时,无需操作,直接返回。时间复杂度为O(1)   。

  1. public void clear() {  
  2.         seqList = new char[MAXLEN];  
  3.         cursor = -1;  
  4.         tail = 0;  
  5.     }  

清空操作比较简单,不进行说明,时间复杂度为O(1)  。

  1. public boolean isEmpty() {  
  2.         if (tail == 0){  
  3.             return true;  
  4.         }  
  5.         return false;  
  6.     }  
  7. public boolean isFull() {  
  8.         if (tail == seqList.length){  
  9.             return true;  
  10.         }  
  11.         return false;  
  12.     }  

根据指向顺序数组的尾部的变量来判断顺序数组是否为空或者满,时间复杂度为O(1)  。

  1. public boolean gotoBeginning() {  
  2.         if (this.isEmpty()){  
  3.             return false;  
  4.         }  
  5.         cursor = 0;  
  6.         return true;  
  7.     }  
  8. public boolean gotoEnd() {  
  9.         if (this.isEmpty()){  
  10.             return false;  
  11.         }  
  12.         cursor = tail-1;  
  13.         return true;  
  14.     }  
  15. public boolean gotoNext() {  
  16.         if (cursor == tail-1){  
  17.             return false;  
  18.         }  
  19.         cursor++;  
  20.         return true;  
  21.     }  
  22. public boolean gotoPrev() {  
  23.         if (cursor <= 0){  
  24.             return false;  
  25.         }  
  26.         cursor--;  
  27.         return true;  
  28.     }  

以上方法都是改变cursor指向位置,不改变元素,因此处理方法大同小异,需要注意空数组,满数组等特殊情况即可。时间复杂度为O(1)  。

  1. public char getCursor() {  
  2.         return seqList[cursor];  
  3.     }  

返回即可,时间复杂度为O(1)  。

  1. public void showStructure(){  
  2.     if (tail == 0){  
  3.         System.out.printf("Empty list -1%n");  
  4.         return;  
  5.     }  
  6.     for(int i=0;i<tail;i++){  
  7.         System.out.printf("%c ",seqList[i]);  
  8.     }  
  9.     System.out.printf("%d%n",cursor);  
  10. }

展示顺序数组所含元素以及当前指向元素位置,时间复杂度为O(n)

2、使用单向链表作为存储表示

数据设计:

  1. //    成员变量  
  2.     public char element = ' ';//选择一个没有意义的字符作为初始化字符  
  3.     public LList next = null;  
  4.   
  5. //    类变量  
  6.     static public int cursor = -1;   //记录当前元素的位置的前一个位置  
  7.     static public LList dummy = new LList();  
  8.     static public LList head = dummy;//    初始化为哑节点  
  9.     static public LList cur = dummy;//指向当前元素的指针,初始化为哑节点  

数据分为成员变量和类变量,注释中已说明具体含义。需要重点说明的一点为,我编写的单向链表采用了哑节点的方式,虽然会带来操作上的一些理解困难,但是减少了一些操作的时间。需要和顺序数组区分的是,以链表实现的存储表示不需要提前确定最大容量。

算法设计:

  1.     public void insert(char newElement) {  
  2.         LList tem = new LList();  
  3.         tem.element = newElement;  
  4. //        判断是否为空,是的话直接插入  
  5.         if (isEmpty()){  
  6.             cur.next = tem;  
  7.             return;  
  8.         }  
  9. //        判断是否在尾部,是的话直接插入并且转化cur  
  10.         if (isFull()){  
  11.             cur.next.next = tem;  
  12.             cur = cur.next;  
  13.             return;  
  14.         }  
  15. //        后面有元素  
  16.         tem.next = cur.next.next;  
  17.         cur.next.next = tem;  
  18.         cur = cur.next;  
  19.     }  

插入操作分为三种情况:空数组,满数组,正常插入。由于使用了链表结构,插入操作难度大幅降低,只需要把需要插入位置前的元素指向插入元素,插入元素指向插入位置后的元素。因此,时间复杂度为O(1)  。

  1. public void remove() {  
  2.         if (isEmpty()){  
  3.             return;  
  4.         }  
  5.         if (isFull()){  
  6.             cur.next = null;  
  7.             cur = dummy;  
  8.             return;  
  9.         }  
  10.         cur.next = cur.next.next;  
  11.     }  

删除操作与插入操作大同小异,也是三种情况:空数组,满数组,正常删除。但是,由于采用了哑节点技术,使得我们删除操作得到了极大的优化,只需要把cur指针往前移即可。

因此,时间复杂度为O(1)  。

  1. public void replace(char newElement) {  
  2.         if (cur.next == null){  
  3.             return;  
  4.         }  
  5.         cur.next.element = newElement;  
  6.     }  

代替操作不能直接代替,需要先判断cur.next是否为null。因此,时间复杂度为O(1)  。

  1. public void clear() {  
  2.         cur =dummy;  
  3.         dummy.next = null;  
  4.     }  

清除操作只需要简单地改变两个类变量的值即可。因此,时间复杂度为O(1)  。

  1. public boolean isEmpty() {  
  2.         return head.next == null;  
  3.     }  
  4. public boolean isFull() {  
  5.         return cur.next.next == null;  
  6.     }  

判断是否为空数组和满数组时,只需要看各个指针的情况即可。因此,时间复杂度为O(1)  。

  1. public boolean gotoBeginning() {  
  2.         if (isEmpty()){  
  3.             return false;  
  4.         }  
  5.         cur = head;  
  6.         return true;  
  7.     }  
  8. public boolean gotoEnd() {  
  9.         if (isEmpty()){  
  10.             return false;  
  11.         }  
  12.         while (cur.next.next != null){  
  13.             cur = cur.next;  
  14.         }  
  15.         return true;  
  16.     }  
  17. public boolean gotoNext() {  
  18.         if (isEmpty()){  
  19.             return false;  
  20.         }  
  21.         if (isFull()){  
  22.             return false;  
  23.         }  
  24.         cur = cur.next;  
  25.         return true;  
  26.     } public boolean gotoPrev() {  
  27.         if (isEmpty()){  
  28.             return false;  
  29.         }  
  30.         if (cur == head){  
  31.             return true;  
  32.         }  
  33.         LList tem = head;  
  34.         while (tem.next != cur){  
  35.             tem = tem.next;  
  36.         }  
  37.         cur = tem;  
  38.         return true;  
  39.     }  

以上方法都是改变cur的指向位置,不改变元素,因此处理方法大同小异,需要注意空数组,满数组,在第一元素时不能向前移动,在最后一个元素不能向后移动等特殊情况即可。时间复杂度为O(1)  。

  1. public char getCursor() {  
  2.         return cur.next.element;  
  3.     }  

返回指向元素,时间复杂度为O(1)  。

  1. public void showStructure() {  
  2.         if(isEmpty()){  
  3.             System.out.printf("Empty list -1%n");  
  4.             return;  
  5.         }  
  6.         LList tem = head.next;  
  7.         while (tem.next != null){  
  8.             System.out.printf("%c ",tem.element);  
  9.             tem = tem.next;  
  10.         }  
  11.         cursor = getIntCursor();  
  12.         System.out.printf("%c %d%n",tem.element,cursor);  
  13.     }  

展示单向数组所含元素以及当前指向元素位置,cursor是获取当前指向元素在单向链表的下标,因此可以知道时间复杂度为O(n)。同时展示元素要一一遍历,时间复杂度为O(n)。

3、使用双向链表作为存储表示

数据设计:

  1. //    成员变量  
  2. public char element = ' ';//选择一个没有意义的字符作为初始化字符  
  3. public DList next = null;  
  4. public DList pre = null;  
  5.   
  6. //    类变量  
  7. static public int cursor = -1;   //记录当前元素的位置,无需提前一个位置  
  8. static public DList head = null;//    初始化  
  9. static public DList cur = head;//指向当前元素的指针  

双向链表的数据变量分为成员变量和类变量,具体含义在注释中已说明,不赘述。需要注意的是,在双向链表中,由于双向链表的特性,没有采用哑节点,指针指向当前元素的位置。

算法设计:

  1. public void insert(char newElement) {  
  2.     DList tem = new DList();  
  3.     tem.element = newElement;  
  4.       判断是否为空,是的话开始创建  
  5.     if (isEmpty()){  
  6.         cur = tem;  
  7.         head =cur;  
  8.         return;  
  9.     }  
  10.     tem.next = cur.next;  
  11.     tem.pre = cur;  
  12.     cur.next = tem;  
  13.     if (tem.next!=null){  
  14.         tem.next.pre = tem;  
  15.     }  
  16.     cur = tem;  
  17. }  

插入操作分为三种情况:空数组,正常插入。操作与单向链表类似,但是需要进行tem.next!=null判断,避免满数组插入时导致空指针错误,总而言之,双链表的插入操作时间复杂度为O(1)  。

  1. public void remove() {  
  2.     if (isEmpty()){  
  3.         return;  
  4.     }  
  5.     if (cur == head){  
  6.         cur = head.next;  
  7.         head = cur;  
  8.         return;  
  9.     }  
  10.     if (isFull()){  
  11.         cur.pre.next = null;  
  12.         cur = head;  
  13.         return;  
  14.     }  
  15.     cur.next.pre = cur.pre;  
  16.     cur = cur.next;  
  17.     cur.pre.next = cur;  
  18. }  

由于没有采用哑节点,因此需要判断的条件相较于单向链表较多,一共有四种情况:空列表,只有一个元素的列表,满列表,正常插入。尽管情况较多,但由于双向链表有指向前一元素的指针,因此删除操作只需要把前后元素“相连”,时间复杂度为O(1)  。

  1. public void replace(char newElement) {  
  2.     if (isEmpty()){  
  3.         return;  
  4.     }   

替换操作,时间复杂度为O(1)  。

  1. public void clear() {  
  2.     head = null;  
  3.     cur = head;  
  4. }  
  5. public boolean isEmpty() {  
  6.     return cur == null;  
  7. }  
  8. public boolean isFull() {  
  9.     return cur==null||cur.next==null;  
  10. }  

如上操作,都是对head,cur指针进行判断或者赋值,时间复杂度为O(1)  。

  1. public boolean gotoBeginning() {  
  2.     if (isEmpty()){  
  3.         return false;  
  4.     }  
  5.     cur = head;  
  6.     return true;  
  7. }  
  8. public boolean gotoEnd() {  
  9.     if (isEmpty()){  
  10.         return false;  
  11.     }  
  12.     while (cur.next != null){  
  13.         cur = cur.next;  
  14.     }  
  15.     return true;  
  16. }  
  17. public boolean gotoNext() {  
  18.     if (isEmpty()){  
  19.         return false;  
  20.     }  
  21.     if (isFull()){  
  22.         return false;  
  23.     }  
  24.     cur = cur.next;  
  25.     return true;  
  26. }  
  27. public boolean gotoPrev() {  
  28.     if (isEmpty()){  
  29.         return false;  
  30.     }  
  31.     if (cur == head){  
  32.         return true;  
  33.     }  
  34.     cur = cur.pre;  
  35.     return true;  
  36. }  

如上操作均为改变cur指针位置,只需要注意空数组,满数组等特殊情况即可。时间复杂度为O(1)  。

  1. public char getCursor() {  
  2.     return cur.element;  
  3. }  

返回cur指针当前元素即可,时间复杂度为O(1)  。

  1. public void showStructure() {  
  2.     if(isEmpty()){  
  3.         System.out.printf("Empty list -1%n");  
  4.         return;  
  5.     }  
  6.     DList tem = head;  
  7.     while (tem.next != null){  
  8.         System.out.printf("%c ",tem.element);  
  9.         tem = tem.next;  
  10.     }  
  11.     cursor = getIntCursor();  
  12.     System.out.printf("%d%n",cursor);  
  13. }  

与单向链表相似,不再赘述,时间复杂度为O(n)。


通过上述描述,时间复杂度整理成如下表格:

以我现在的角度来看,我感觉当时写的时间复杂度有问题!

测试:

根据题目要求,编写了测试类(见附录),下面就关键代码进行说明:

  1. switch (tem){  
  2.     case '+':  
  3.         tem = row.charAt(pivot++);  
  4.         list.insert(tem);  
  5.         break;  
  6.     case '-':  
  7.         list.remove();  
  8.         break;  
  9.     case '=':  
  10.         tem = row.charAt(pivot++);  
  11.         list.replace(tem);  
  12.         break;  
  13.     case '#':  
  14.         list.gotoBeginning();  
  15.         break;  
  16.     case '*':  
  17.         list.gotoEnd();  
  18.         break;  
  19.     case '>':  
  20.         list.gotoNext();  
  21.         break;  
  22.     case '<':  
  23.         list.gotoPrev();  
  24.         break;  
  25.     case '~':  
  26.         list.clear();  
  27.         break;  
  28.     default:  
  29.         break;  
  30. }

这是测试类最关键的代码,均按照题目要求进行相应的操作,这里的tem代表读取txt文件中的单个字符。

  1. Scanner in = new Scanner(new File("src/test.txt"));  
  2. Scanner in2 = new Scanner(new File("src/list_result.txt"));  

读取相应文件,一边后面的操作和校对答案。

  1. myAns = list.toString(); 

myAns为字符串,由于showStructure无返回值,不适合大量校准答案,因此我在顺序数组,单向链表,双向链表都重写了toString方法,这样子可以返回String类型,适合校准答案,返回的字符串为showStructure输出的字符串。

  1. boolean equals = myAns.strip().equals(ans.strip());  

这里使用了strip()方法,处理字符串后面的空格和换行符。

测试结果:

  1. 经检验测试结果和标准答案:true  
  2. 经检验测试结果和标准答案:true  
  3. 经检验测试结果和标准答案:true  

三种数据结构均为正确。

总结与收获:

通过上面三种数据结构的编写,我充分理解了顺序数组,单向链表和双向链表的构成,具体实现代码和时间空间的优缺点。

其中最让我感到印象深刻的就是单向链表的哑节点技术的应用,如果让没有听课的我进行单向链表的实现可能就不会采用哑节点技术,这将导致单向链表的删除操作的时间复杂度增加到O(n),这从性能的变化来讲是十分差劲的,因此一个小小的编程思想的影响导致时间复杂度大大降低,让我印象深刻。但是,我也注意到了,当我为了实现哑节点,增加了不少常系数时间的判断,这可能在数据量较少的情况下不如不使用哑节点。

然后,我还想说,双向链表的设计存在十分不足,我没有使用任何技巧,完全采用了个人的理解,这是因为作业的时间紧迫导致的,我现在想来也许可以增加两个无用的节点,大大简化判断流程和代码编写,希望未来有时间可以实现。

 

任务 2:栈和递归之间的关系

题目:略

1、使用递归思想,编写一个函数 permutationByRecursion,该函数用来生成给定的字符

串的全排列结果。

数据说明:

  1. static String str;  
  2. static char[] res; 

str为录入的字符串,但是不方便处理,更改其中的字符。

res为结果字符数组,因为是字符数组方便输出,更改字符,交换字符。

算法说明:

  1. res = new char[str.length()];  
  2. for(int i = 0;i<str.length();i++){  
  3.     res[i] = str.charAt(i);  
  4. }  

把字符串转换为字符数组,方便后续操作。

  1. public static void permutationByRecursion(int left,int right){  
  2.     if (left == right){  
  3.         System.out.print(res);  
  4.         System.out.print(" ");  
  5.     }  
  6.     for(int i=left;i<=right;i++){  
  7.         swap(res,left,i);  
  8.            //进入递归  
  9.         permutationByRecursion(left+1,right);  
  10.         swap(res,left,i);  
  11.     }  
  12. }  

1、基准情况就是当left == right,这个时候输出res,即一种情况。

2、输入时left=0,right=数组长度-1,因此第一次的for循环作用就是遍历数组,这保证了后面输出结果时不遗漏。每次操作如下:将i与left交换,然后再将剩下的left+1到right递归全排序,递归完成后再交换回来。在递归时会往基准情况靠近,因此避免了死递归,而且当到达基准情况时就会输出一种不重复全排序的情况,这是因为递归完成交换回来的操作。

3、全排序的时间复杂度明显为O(n!),而且无任何优化空间。

测试结果:

1、测试用例"abd",结果如下:

acd adc cad cda dca dac

2、测试用例"abcde",结果如下:

abcde abced abdce abdec abedc abecd acbde acbed acdbe acdeb acedb acebd adcbe adceb adbce adbec adebc adecb aecdb aecbd aedcb aedbc aebdc aebcd bacde baced badce badec baedc baecd bcade bcaed bcdae bcdea bceda bcead bdcae bdcea bdace bdaec bdeac bdeca becda becad bedca bedac beadc beacd cbade cbaed cbdae cbdea cbeda cbead cabde cabed cadbe cadeb caedb caebd cdabe cdaeb cdbae cdbea cdeba cdeab ceadb ceabd cedab cedba cebda cebad dbcae dbcea dbace dbaec dbeac dbeca dcbae dcbea dcabe dcaeb dceab dceba dacbe daceb dabce dabec daebc daecb decab decba deacb deabc debac debca ebcda ebcad ebdca ebdac ebadc ebacd ecbda ecbad ecdba ecdab ecadb ecabd edcba edcab edbca edbac edabc edacb eacdb eacbd eadcb eadbc eabdc eabcd

2、使用栈数据结构,将1中编写的算法转换成非递归函数

分析:本题与上课讲的实例汉诺塔基本一致,上课讲的汉诺塔是第一步将A柱上的n-1个盘子借助C柱移向B柱,第二步将A柱上仅剩的最后一个盘子移向C柱,第三步将B柱上的n-1个盘子借助A柱移向C柱,当改为栈结构时,需要再创建一个辅助类。但是,由于本题的特殊性,即字符串的全排序就是数字的全排序,可以进一步转化为数组下标的全排序,这样省去了创建一个类的工作,下面介绍“取巧”做法。

数据结构:

由于题目要求,我们创建了属于自己的栈:

  1. public class MyStack {  
  2.     public int[] listArr;  
  3.     private int tail = -1;//不仅是指向尾部的指针,也代表了元素大小  
  4.     public MyStack()  
  5.     public MyStack(int MAX)  
  6.     public void push(int t)  
  7.     public int pop()  
  8.     public boolean isHas(int x  
  9.     public boolean isEmpty()  
  10.     public void printAns(String s)  
  11. }  

具体的实现过程不再给出,详细的实现过程可以查看附录代码,我详细说明一下变量含义和方法参数及返回值和作用。

1、变量。有int[] listArr这个是数组,用来存储数据,充当栈。int tail如注释所描述,不赘述。

2、各个方法。pop是出栈并且返回整数,push是入栈,isHas是判断栈中是否有参数x,isEmpty是判断栈是否为空,空返回true,反之。printAns是根据栈中的数字按照不同顺序输出字符串s。

算法设计:

下面给出核心代码:

  1. while (!myStack.isEmpty()){  
  2.     int i = myStack.pop() + 1;//加一是为了与其位置匹配  
  3.     while (i < s.length()){  
  4.         if (!myStack.isHas(i)){  
  5.             myStack.push(i);  
  6.             //寻找未进栈的元素进栈  
  7.             for (int j = 0; j < s.length(); j++){  
  8.                 if (!myStack.isHas(j)){  
  9.                     myStack.push(j);  
  10.                 }  
  11.             }  
  12.             myStack.printAns(s);  
  13.             break;  
  14.         }  
  15.         i++;  
  16.     }  
  17. }  

这是最关键的代码段,其核心作用就是将栈中数字的排序从小到大改为从大到小,而且每当出现一种新的排序时就输出字符串。

现在来具体讲解。

1、出栈元素,要加1是为了与其位置匹配。

2、进行循环,判断栈中是否含有i,若不含有则压入栈,注意我们虽然压入的都是i,但是i已经进行了+1操作,所以实现了从大到小的变换。接着,寻找未进栈的元素并且将其压入栈,注意这里要从0到n,因为这样子保证了从小到大压入,保证了操作的一致性。当全部压入后,则找到了一个全新的排序,输出,停止循环。

3、若栈中含有i,则说明出栈元素前的栈中存在一个比出栈元素大一的元素(有点拗口),则通过不断地i++把这个i排除掉,然后进行下一次的while (!myStack.isEmpty())循环操作。

总的来说,该算法实现了数字排序从小到大改为从大到小。由于我们录入数据是0-n,因此在排序过程中一定实现了全排序。

测试:

测试类不再给出,较简单。

测试结果:

测试用例“abc”,结果如下:

abc acb bac bca cab cba

测试用例“abcd”,结果如下:

abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba dabc dacb dbac dbca dcab dcba

变形 1:当字符串中出现相同字符时,只给出完全不一样的排列组合

设计:数据设计,算法设计与1中的递归设计基本一致,只需要在递归之前加入相同字符不交换递归即可,如下代码。

  1. if (res[left]!=res[i]||i==left){  
  2.     swap(res,left,i);  
  3.     permutationByRecursion(left+1,right);  
  4.     swap(res,left,i);  
  5. }  

测试:

1、测试用例“aac”,结果如下:

aac aca caa

2、测试用例“aaaa”,结果如下:

aaaa

3、测试用例“aabc”,结果如下:

aabc aacb abac abca acba acab baac baca bcaa caba caab cbaa

(有些测试用例输出过大,附录和这里都不在给出,可以通过测试类测试)

变形 2:输出长度为 n 的字符串中取 k 个字符构成的所有全排列。

分析:因为在1递归中,直接定好了char[] res的长度,导致只能输出n个字符,而题目要求输出k个因此需要在后续进行修改,增加新的变量。同时,1中的递归思想和方法大体不变,只需要对具体实现过程进行小幅修改,改变递归基准条件,使其能取k个字符后输出而且保证是全排序。

数据设计:

  1. static String str;  
  2. static char[] strArr;  
  3. static char[] res;  

str还是录入的字符串

strArr是将录入的字符串转为字符数组

res和1递归中的res作用一致,不过长度不再为n,而为k

算法设计:

  1.     private static void permutationByRecursion(int k){  
  2. //        递归条件  
  3.         if (k==0){  
  4.             System.out.print(res);  
  5.             System.out.print(" ");  
  6.             return;  
  7.         }  
  8. //        把不重复字符加入(十分暴力^_^)  
  9.         for(char strArr:strArr){  
  10.             if (!has(res,strArr)){  
  11.                 res[res.length-k] = strArr;  
  12.             } else{  
  13.                 continue;  
  14.             }  
  15. //            进入递归  
  16.             permutationByRecursion(k-1);  
  17. //            重新置为表示字符  
  18.             res[res.length - k] = ' ';  
  19.         }  
  20.     }  

1、基准条件改为k==0,这与1递归中的left==right的设计思想是一模一样的;

2、第二步,把不重复的字符加入。has(res,strArr)这个函数是判断res字符数组中是否有与strArr字符一致的字符,若无,加入到res数组里面;若有,则忽略,继续。然后进入递归。最后再将其置为初始化的字符‘ ’。上述步骤与1递归中的先交换再递归再交换的核心思想是一模一样的,因此可以保证该算法的正确性,同时可以知道该算法的前置条件为一为无重复字符元素,二为‘ ’字符不参与排序。

3、总的来说,这个算法虽然具体实现过程相差1递归的程序有相差,但是,核心思想不变,都是先提取一个字符,让剩下的n-1个字符进行递归,当剩余元素为0时,达到基准情况返回一种全排序结果,而且由于for循环整个数组和递归完后的“善尾工作”——再初始化或者再交换,可以保证全排序的结果不重复不遗漏。

4、时间复杂度为题目中公式所示,证明略。

测试:

测试用例“abcd” 2 ,结果如下:

ab ac ad ba bc bd ca cb cd da db dc

测试用例“abcde” 4 结果如下:

abcd abce abdc abde abec abed acbd acbe acdb acde aceb aced adbc adbe adcb adce adeb adec aebc aebd aecb aecd aedb aedc bacd bace badc bade baec baed bcad bcae bcda bcde bcea bced bdac bdae bdca bdce bdea bdec beac bead beca becd beda bedc cabd cabe cadb cade caeb caed cbad cbae cbda cbde cbea cbed cdab cdae cdba cdbe cdea cdeb ceab cead ceba cebd ceda cedb dabc dabe dacb dace daeb daec dbac dbae dbca dbce dbea dbec dcab dcae dcba dcbe dcea dceb deab deac deba debc deca decb eabc eabd eacb eacd eadb eadc ebac ebad ebca ebcd ebda ebdc ecab ecad ecba ecbd ecda ecdb edab edac edba edbc edca edcb

有些用例的输出过大,附录和这里都不在给出,可以尝试使用附录的测试类测试。

总结与收获:

在本次任务中,我编写了字符串的全排序问题,并且进行相应的改善,这是十分有趣的。下面我对三次改善谈谈自己的收获。

首先,就是把递归改成了栈。在这一问题上,我花费了不小的力气,说实话,我上课听懂了汉诺塔,可后面自己写的时候,常常看着递归的代码想不出来怎么改成栈,不知道怎么组织类的结构来进行更改,因此我放弃了老师上课讲的汉诺塔改为栈的方法,当然我希望老师能把上课讲的汉诺塔改成栈的具体代码发给我,嘿嘿。言归正传,我在网上找到了更巧妙地改为栈的方法,加以自己的理解写出了如上代码,在编写过程中,我还看了几次网上的解释才理解通透,可以说这个递归改成栈是十分困难的,今后要多下功夫了。

然后是两次更改递归条件的问题。第一个主要判断是否相同,不相同再调用递归,就达到了问题要求,一样的输出只输出一次。第二个,由于一开始写程序就定死了输出个数,因此我按照相同的逻辑把具体实现过程进行了更改,改成了可以输出个数变化的情况。

任务 3:创建一个可自动调整空间大小的Queue 数据结构

题目:略

数据设计:

  1. private int maxSize = 2;  
  2. private int front = 0;  
  3. private int rear = 0;  
  4. private T[] listArray =(T[]) new Object[2]; 

下面对其变量进行说明。

maxSize为当前队列所能容纳的最多元素,这里需要说明因为采用了循环数组,因此能存储的元素数量总是小于maxSize,等于maxSize-1,这对于后面的程序编写会有一定的影响;front可以理解为队头指针,为了判断满队列,空队列,需要队头指针的前一位才是储存元素的首元素;

rear可以理解为队尾指针;

listArray就是数组,大小初始化为2。

算法设计:

  1. public ResizingQueue(){}   

构造方法,无需指定大小。

  1. public void enqueue(T element) throws IllegalArgumentException{  
  2.     if (element == null){  
  3.         throw new IllegalArgumentException();  
  4.     }  
  5.     if (isExpand()){  
  6.           expand()方法改变了大小,改变了front,rear,maxSize数值  
  7.         listArray = expand(maxSize,(maxSize-1)*2+1);  
  8.     }  
  9.     rear = (rear+1)%maxSize;  
  10.     listArray[rear] = element;  
  11. }  

enqueue方法将元素 element入队,如果队列满,则需要完成空间大小的调整。使用isExpand()方法判断是否为队列满,如果满队列,使用expand方法进行扩充,其中expand的两个参数为变换前的最大容量和变化后的最大容量,该方法返回T数组。扩充完毕后,再进行入队操作。

  1. public T dequeue() throws NoSuchElementException{  
  2.     if (isEmpty()){  
  3.         throw new NoSuchElementException();  
  4.     }  
  5.     listArray[front] = null;  
  6.     front = (front+1)%maxSize;  
  7.     T res =listArray[front];  
  8.     if (isShrink()){  
  9.         listArray = shrink(maxSize,(maxSize-1)/2);  
  10.     }  
  11.     return res;  
  12. }

dequeue()从队列中将队头元素删除并返回,如果队列的元素个数是当前容量的 1/4,那么完成空间大小的调整。首先进行空数组判断,若为空数组,抛出NoSuchElementException。若不为空数组,则先进行删除,再判断是否需要收缩数组。isShrink()判断队列的元素个数是当前容量的 1/4,若满足,则调用shrink方法,shrink方法的第一个参数为当前最大容量,后一个为收缩完成后的最大容量,返回值为T数组。

  1. public int size(){  
  2.     if (rear>=front){  
  3.         return rear-front;  
  4.     }  
  5.     return maxSize-front+rear;  
  6. }  

由于使用了循环数组,因此通过front和rear的两种情况的相对位置即可判断数组容量。

  1. public String toString() {  
  2.     String res = "";  
  3.     res += "[";  
  4.       不大于20的情况  
  5.     if (this.size()<=20){  
  6.         if (rear>=front){  
  7.             for (int i = front+1;i<=rear;i++){  
  8.                 res = res + listArray[i] + " ";  
  9.             }  
  10.         }  
  11.         else {  
  12.             for (int i = front+1;i<maxSize;i++){  
  13.                 res = res + listArray[i] + " ";  
  14.             }  
  15.             for (int i = 0;i<=rear;i++){  
  16.                 res = res + listArray[i] + " ";  
  17.             }  
  18.         }  
  19.     }  
  20.       大于20的情况  
  21.     else {  
  22.         int tem = (front+1)%maxSize;  
  23.         int count = 0;  
  24.           去前五个元素  
  25.         do{  
  26.             res = res + listArray[tem] + " ";  
  27.             tem = (tem+1)%maxSize;  
  28.             count++;  
  29.         }  
  30.         while (count<5);  
  31.         res += " ... ";  
  32.           取后五个元素  
  33.         while (count<this.size()-5){  
  34.             tem = (tem+1)%maxSize;  
  35.             count++;  
  36.         }  
  37.         while (count<this.size()){  
  38.             res = res + listArray[tem] + " ";  
  39.             tem = (tem+1)%maxSize;  
  40.             count++;  
  41.         }  
  42.     }  
  43.     res = res.strip()+"]";  
  44.     res += "\nelements: " + this.size() + " size:"+(this.maxSize-1);  
  45.     return res;  
  46. }  

虽然看上去比较“冗杂”,但都是针对不同情况的重复性的工作,按照题目要求分为不大于20的表示方式和大于20的表示方式,由于采用了循环数组,因此必须判断rear和front的相对位置。

测试:

编写了测试类(见附录),下面对关键代码进行说明:

  1. Scanner in = new Scanner(new File("src/test5000.txt"));  
  2. Scanner in2 = new Scanner(new File("src/ result5000.txt")); 

读取txt文件,便于后续操作。

  1. if (in.hasNextInt()){  
  2.     nextInt = in.nextInt();  
  3. //  System.out.printf("读取到的数字为:%d",nextInt);  
  4.     resizingQueue.enqueue(nextInt);  
  5. }

这是当读取到数字时,采用入队操作。

  1. if (nextChar == '-'){  
  2.     resizingQueue.dequeue();  
  3. }  
  4. if (nextChar == '?'){  
  5.     System.out.println(resizingQueue.toString());  
  6.     String tem1 = in2.nextLine();  
  7.     String tem2 = in2.nextLine();  
  8.     String res = tem1 + "\n" + tem2;  
  9.     System.out.println("res为:"+res);  
  10.     boolean temVertify = res.strip().equals(resizingQueue.toString().strip());  
  11.     System.out.println("比较结果为:"+temVertify);  
  12.     vertify = vertify && temVertify;  
  13. }  

当读取到’-’时,使用出队操作。当读取到’?’,需要调用toString方法,同时与结果进行比较。

测试结果:

  1. 经检验输出的字符串与结果符合判定:true  
  2. 经检验输出的字符串与结果符合判定:true  

result1000.txt result5000.txt分别调用测试类,结果如上。

总结与收获:

队列的编写有了前面顺序数组,单向链表,双向链表代码编写的经验,写起来速度了不少,遇到的困难和小问题都在调试的过程中解决了,总体来说,编程思想和前面三个有相似之处。

任务 4:基数排序

题目: 略

1、当数据序列是整数类型的数据的时候,数据序列中每个数据的位数不要求等宽

数据设计:

  1. static ResizingQueue[] resizingQueue = new ResizingQueue[10];  

数组大小为10是因为数字是0-9。

算法设计:

  1.     static void sort(int[] arr,int len){  
  2. //        初始化队列  
  3.         for(int i=0;i<resizingQueue.length;i++){  
  4.             resizingQueue[i] = new ResizingQueue<Integer>();  
  5.         }  
  6. //        变量+初始化  
  7.         boolean flag = true;  
  8.         int modNum = 0;  
  9.         int tem=0;  
  10. //        一位一位操作,直到最长的数字结束  
  11.         while (flag){  
  12.             modNum++;  
  13. //            入队,将数组数字按照顺序入队  
  14.             for (int i=0;i<len;i++){  
  15.                 int pivot = mod(arr[i],modNum);  
  16.                 tem = Math.max(tem,pivot);  
  17.                 resizingQueue[pivot].enqueue(arr[i]);  
  18.                 flag = (tem != 0);  
  19.             }  
  20.             tem = 0;  
  21.             int arrPivot = 0;  
  22. //            出队,重新给数组赋值  
  23.             for (int i=0;i<resizingQueue.length;i++){  
  24.                 while (!resizingQueue[i].isEmpty()){  
  25.                    arr[arrPivot++] = (int)resizingQueue[i].dequeue()
  26.                 }  
  27.             }  
  28.         }  
  29. //        输出  
  30.         for (int i=0;i<len;i++){  
  31.             System.out.printf("%d ",arr[i]);  
  32.         }  
  33.     } 

注释已经有部分讲解,下面进行更详细的讲解。

1、sort方法的第一个参数为数组,第二个参数为数组的长度。

2、定义了几个变量,进行说明。flag判断是否继续进行排序操作,当所有数字取模均为0时就不再继续;modNum记录取模次数,为mod方法的第二个参数;tem仅为辅助变量,改变flag的值。

3、入队操作,按照数字的每位数取模后的大小进入相应的队列数组中。

4、出队操作,按照顺序从队列数组的第0位到第9位出队,由于是队列,遵守先进先出原则,因此满足先后顺序。

5、当所有数字取模均为0时,数组有序,完成排序。由于数字量远远大于桶的数量(队列数组大小),因此时间复杂度为O(n)。

测试:

编写了主函数作为测试(见附录),主要思想为把文件中数字放入数组中,再调用sort方法。下面进行主要代码分析。

  1. while (in.hasNextInt()){  
  2.     int tem = in.nextInt();  
  3.     System.out.println(tem);  
  4.     arr[pivot++] = tem;  
  5. }  

当存在下一个数字时,存入数组。记录位置。

测试结果:

见附录

2、当数据序列是字符串类型的数据的时候,数据序列中每个字符串都是等宽的

数据设计:

  1. static ResizingQueue[] resizingQueue = new ResizingQueue[26];  

数组大小为26是因为字母是a-z。由于题目未明确说明,我把大写字母和小写字母视为一致。

算法设计:

  1.     static void sort(String[] arr,int len){  
  2. //        初始化队列数组  
  3.         for(int i=0;i<resizingQueue.length;i++){  
  4.             resizingQueue[i] = new ResizingQueue<String>();  
  5.         }  
  6. //        因为等宽,所以取第一个字符串长度作为长度  
  7.         int stringLen = arr[0].length();  
  8.         int tem=0;  
  9.         for (int k=0;k<stringLen;k++){  
  10. //            入队操作  
  11.             for (int i=0;i<len;i++){  
  12.                 int pivot = arr[i].toLowerCase().charAt(stringLen-k-1)-'a';  
  13.                 resizingQueue[pivot].enqueue(arr[i]);  
  14.             }  
  15.             tem = 0;  
  16.             int arrPivot = 0;  
  17. //            出队操作  
  18.             for (int i=0;i<resizingQueue.length;i++){  
  19.                 while (!resizingQueue[i].isEmpty()){  
  20.                     arr[arrPivot++] = (String) resizingQueue[i].dequeue();  
  21.                 }  
  22.             }  
  23.         }  
  24.         for (int i=0;i<len;i++){  
  25.             System.out.printf(arr[i]+" ");  
  26.         }  
  27.     }

sort方法的参数,步骤和核心思想与数字的基数排序基本一致,不再赘述,下面讲几个相异点:

1、由于字符串长度一致,因此不需要数字基数排序中的flag。

2、由于不区分大小写字母,因此先取小写(toLowerCase()的方法)再比较。

3、数字是依靠模运算取位数,字符串是运用charAt()方法取字符串的单个字符。

测试:

       编写了主函数作为测试,核心思想为把文件中字符串放入数组中,再调用sort方法。下面进行关键代码分析。

  1. while (in.hasNext()){  
  2.     arr[pivot++] = in.next();  
  3. }  

当存在下一个字符串时,把字符串存入数组,pivot记录存入下标。

测试结果:

见附录

总结与收获:

基数排序还是基于桶排序,按照从低位到高位依次桶排序,最后输出结果即可,可以注意到桶的个数,位数的个数,数据量共同决定的时间复杂度,这与课上学习的一致,强化了学习结果。

附录

任务一

  1. public class SeqList implements List{  
  2.     /*        这个是顺序数组实现的数据结构     */  
  3.     private int MAXLEN;  
  4.     private char[] seqList = null;  
  5.     private int cursor = -1;  
  6.     private int tail = 0;  
  7.   
  8. //  构造函数  
  9.     public SeqList(){  
  10.         this(10000);  
  11.     }  
  12.     public SeqList(int MAXLEN){  
  13.         this.MAXLEN = MAXLEN;  
  14.         seqList = new char[MAXLEN];  
  15.     }  
  16.   
  17.     @Override  
  18.     public void insert(char newElement) {  
  19.         //  after the cursor  
  20.         if (tail>=MAXLEN){  
  21.             return;  
  22.         }  
  23. //        空数组插入  
  24.         if (tail==0){  
  25.             cursor++;  
  26.             seqList[cursor] = newElement;  
  27.             tail++;  
  28.             return;  
  29.         }  
  30. //        非空插入  
  31.         for (int i=tail-1;i>cursor;i--){  
  32.             seqList[i+1] = seqList[i];  
  33.         }  
  34.         cursor++;  
  35.         seqList[cursor] = newElement;  
  36.         tail++;  
  37.     }  
  38.   
  39.     @Override  
  40.     public void remove() {  
  41.         tail--;  
  42.         //        空数组  
  43.         if (tail <= 0){  
  44.             tail = 0;  
  45.             cursor = -1;  
  46.             seqList = new char[MAXLEN];  
  47.             return;  
  48.         }  
  49.         for(int i=cursor;i<tail;i++){  
  50.             seqList[i] = seqList[i+1];  
  51.         }  
  52.         cursor = cursor%tail;  
  53.     }  
  54.   
  55.     @Override  
  56.     public void replace(char newElement) {  
  57.         if (cursor < 0){  
  58.             return;  
  59.         }  
  60.         seqList[cursor] = newElement;  
  61.     }  
  62.   
  63.     @Override  
  64.     public void clear() {  
  65.         seqList = new char[MAXLEN];  
  66.         cursor = -1;  
  67.         tail = 0;  
  68.     }  
  69.   
  70.     @Override  
  71.     public boolean isEmpty() {  
  72.         if (tail == 0){  
  73.             return true;  
  74.         }  
  75.         return false;  
  76.     }  
  77.   
  78.     @Override  
  79.     public boolean isFull() {  
  80.         if (tail == seqList.length){  
  81.             return true;  
  82.         }  
  83.         return false;  
  84.     }  
  85.   
  86.     @Override  
  87.     public boolean gotoBeginning() {  
  88.         if (this.isEmpty()){  
  89.             return false;  
  90.         }  
  91.         cursor = 0;  
  92.         return true;  
  93.     }  
  94.   
  95.     @Override  
  96.     public boolean gotoEnd() {  
  97.         if (this.isEmpty()){  
  98.             return false;  
  99.         }  
  100.         cursor = tail-1;  
  101.         return true;  
  102.     }  
  103.   
  104.     @Override  
  105.     public boolean gotoNext() {  
  106.         if (cursor == tail-1){  
  107.             return false;  
  108.         }  
  109.         cursor++;  
  110.         return true;  
  111.     }  
  112.   
  113.     @Override  
  114.     public boolean gotoPrev() {  
  115.         if (cursor <= 0){  
  116.             return false;  
  117.         }  
  118.         cursor--;  
  119.         return true;  
  120.     }  
  121.   
  122.     @Override  
  123.     public char getCursor() {  
  124.         return seqList[cursor];  
  125.     }  
  126.   
  127.     @Override  
  128.     public void showStructure(){  
  129.         if (tail == 0){  
  130.             System.out.printf("Empty list -1%n");  
  131.             return;  
  132.         }  
  133.         for(int i=0;i<tail;i++){  
  134.             System.out.printf("%c ",seqList[i]);  
  135.         }  
  136.         System.out.printf("%d%n",cursor);  
  137.     }  
  138.   
  139.     @Override  
  140.     public String toString() {  
  141.         return myResult();  
  142.     }  
  143.   
  144.     public String myResult(){  
  145.         String myAns = "";  
  146.         if (tail == 0){  
  147.             myAns = "Empty list -1 ";  
  148.             return myAns;  
  149.         }  
  150.         for(int i=0;i<tail;i++){  
  151.             myAns += seqList[i];  
  152.             myAns += " ";  
  153.         }  
  154.         myAns += cursor;  
  155.         myAns += " ";  
  156.         return myAns;  
  157.     }  
  158. }  

  1. public class LList implements List {  
  2.   
  3. //    成员变量  
  4.     public char element = ' ';//选择一个没有意义的字符作为初始化字符  
  5.     public LList next = null;  
  6.   
  7. //    类变量  
  8.     static public int cursor = -1;   //记录当前元素的位置的前一个位置  
  9.     static public LList dummy = new LList();  
  10.     static public LList head = dummy;//    初始化为哑节点  
  11.     static public LList cur = dummy;//指向当前元素的指针,初始化为哑节点  
  12.   
  13.     public LList(){  
  14.   
  15.     }  
  16.     @Override  
  17.     public void insert(char newElement) {  
  18.         LList tem = new LList();  
  19.         tem.element = newElement;  
  20. //        判断是否为空,是的话直接插入  
  21.         if (isEmpty()){  
  22.             cur.next = tem;  
  23.             return;  
  24.         }  
  25. //        判断是否在尾部,是的话直接插入并且转化cur  
  26.         if (isFull()){  
  27.             cur.next.next = tem;  
  28.             cur = cur.next;  
  29.             return;  
  30.         }  
  31. //        后面有元素  
  32.         tem.next = cur.next.next;  
  33.         cur.next.next = tem;  
  34.         cur = cur.next;  
  35.     }  
  36.   
  37.     @Override  
  38.     public void remove() {  
  39.         if (isEmpty()){  
  40.             return;  
  41.         }  
  42.         if (isFull()){  
  43.             cur.next = null;  
  44.             cur = dummy;  
  45.             return;  
  46.         }  
  47.         cur.next = cur.next.next;  
  48.     }  
  49.   
  50.     @Override  
  51.     public void replace(char newElement) {  
  52.         if (cur.next == null){  
  53.             return;  
  54.         }  
  55.         cur.next.element = newElement;  
  56.     }  
  57.   
  58.     @Override  
  59.     public void clear() {  
  60.         cur =dummy;  
  61.         dummy.next = null;  
  62.     }  
  63.   
  64.     @Override  
  65.     public boolean isEmpty() {  
  66.         return head.next == null;  
  67.     }  
  68.   
  69.     @Override  
  70.     public boolean isFull() {  
  71.         return cur.next.next == null;  
  72.     }  
  73.   
  74.     @Override  
  75.     public boolean gotoBeginning() {  
  76.         if (isEmpty()){  
  77.             return false;  
  78.         }  
  79.         cur = head;  
  80.         return true;  
  81.     }  
  82.   
  83.     @Override  
  84.     public boolean gotoEnd() {  
  85.         if (isEmpty()){  
  86.             return false;  
  87.         }  
  88.         while (cur.next.next != null){  
  89.             cur = cur.next;  
  90.         }  
  91.         return true;  
  92.     }  
  93.   
  94.     @Override  
  95.     public boolean gotoNext() {  
  96.         if (isEmpty()){  
  97.             return false;  
  98.         }  
  99.         if (isFull()){  
  100.             return false;  
  101.         }  
  102.         cur = cur.next;  
  103.         return true;  
  104.     }  
  105.   
  106.     @Override  
  107.     public boolean gotoPrev() {  
  108.         if (isEmpty()){  
  109.             return false;  
  110.         }  
  111.         if (cur == head){  
  112.             return true;  
  113.         }  
  114.         LList tem = head;  
  115.         while (tem.next != cur){  
  116.             tem = tem.next;  
  117.         }  
  118.         cur = tem;  
  119.         return true;  
  120.     }  
  121.   
  122.     @Override  
  123.     public char getCursor() {  
  124.         return cur.next.element;  
  125.     }  
  126.   
  127.     @Override  
  128.     public void showStructure() {  
  129.         if(isEmpty()){  
  130.             System.out.printf("Empty list -1%n");  
  131.             return;  
  132.         }  
  133.         LList tem = head.next;  
  134.         while (tem.next != null){  
  135.             System.out.printf("%c ",tem.element);  
  136.             tem = tem.next;  
  137.         }  
  138.         cursor = getIntCursor();  
  139.         System.out.printf("%c %d%n",tem.element,cursor);  
  140.     }  
  141.   
  142.     private String myRes(){  
  143.         if(isEmpty()){  
  144.             return "Empty list -1";  
  145.         }  
  146.         String myRes = "";  
  147.         LList tem = head.next;  
  148.         while (tem.next != null){  
  149.             myRes = myRes + " " + tem.element;  
  150.             tem = tem.next;  
  151.         }  
  152.         cursor = getIntCursor();  
  153.         myRes= myRes+" "+tem.element+" "+cursor;  
  154.         return myRes;  
  155.     }  
  156.   
  157.     @Override  
  158.     public String toString() {  
  159.         return myRes();  
  160.     }  
  161.   
  162.     private int getIntCursor(){  
  163.         if (isEmpty()){  
  164.             return -1;  
  165.         }  
  166.         LList tem = head;  
  167.         int intCursor = 0;  
  168.         while (tem.next!=cur.next){  
  169.             intCursor++;  
  170.             tem = tem.next;  
  171.         }  
  172.         return intCursor;  
  173.     }  
  174. }  

  1. public class DList implements List {  
  2.     //    成员变量  
  3.     public char element = ' ';//选择一个没有意义的字符作为初始化字符  
  4.     public DList next = null;  
  5.     public DList pre = null;  
  6.   
  7.     //    类变量  
  8.     static public int cursor = -1;   //记录当前元素的位置,无需提前一个位置  
  9.     static public DList head = null;//    初始化  
  10.     static public DList cur = head;//指向当前元素的指针  
  11.   
  12.     public DList(){  
  13.     }  
  14.     @Override  
  15.     public void insert(char newElement) {  
  16.         DList tem = new DList();  
  17.         tem.element = newElement;  
  18. //        判断是否为空,是的话开始创建  
  19.         if (isEmpty()){  
  20.             cur = tem;  
  21.             head =cur;  
  22.             return;  
  23.         }  
  24.         tem.next = cur.next;  
  25.         tem.pre = cur;  
  26.         cur.next = tem;  
  27.         if (tem.next!=null){  
  28.             tem.next.pre = tem;  
  29.         }  
  30.         cur = tem;  
  31.     }  
  32.   
  33.     @Override  
  34.     public void remove() {  
  35.         if (isEmpty()){  
  36.             return;  
  37.         }  
  38.         if (cur == head){  
  39.             cur = head.next;  
  40.             head = cur;  
  41.             return;  
  42.         }  
  43.         if (isFull()){  
  44.             cur.pre.next = null;  
  45.             cur = head;  
  46.             return;  
  47.         }  
  48.         cur.next.pre = cur.pre;  
  49.         cur = cur.next;  
  50.         cur.pre.next = cur;  
  51.     }  
  52.   
  53.     @Override  
  54.     public void replace(char newElement) {  
  55.         if (isEmpty()){  
  56.             return;  
  57.         }  
  58.         cur.element = newElement;  
  59.     }  
  60.   
  61.     @Override  
  62.     public void clear() {  
  63.         head = null;  
  64.         cur = head;  
  65.     }  
  66.   
  67.     @Override  
  68.     public boolean isEmpty() {  
  69.         return cur == null;  
  70.     }  
  71.   
  72.     @Override  
  73.     public boolean isFull() {  
  74.         return cur==null||cur.next==null;  
  75.     }  
  76.   
  77.     @Override  
  78.     public boolean gotoBeginning() {  
  79.         if (isEmpty()){  
  80.             return false;  
  81.         }  
  82.         cur = head;  
  83.         return true;  
  84.     }  
  85.   
  86.     @Override  
  87.     public boolean gotoEnd() {  
  88.         if (isEmpty()){  
  89.             return false;  
  90.         }  
  91.         while (cur.next != null){  
  92.             cur = cur.next;  
  93.         }  
  94.         return true;  
  95.     }  
  96.   
  97.     @Override  
  98.     public boolean gotoNext() {  
  99.         if (isEmpty()){  
  100.             return false;  
  101.         }  
  102.         if (isFull()){  
  103.             return false;  
  104.         }  
  105.         cur = cur.next;  
  106.         return true;  
  107.     }  
  108.   
  109.     @Override  
  110.     public boolean gotoPrev() {  
  111.         if (isEmpty()){  
  112.             return false;  
  113.         }  
  114.         if (cur == head){  
  115.             return true;  
  116.         }  
  117.         cur = cur.pre;  
  118.         return true;  
  119.     }  
  120.   
  121.     @Override  
  122.     public char getCursor() {  
  123.         return cur.element;  
  124.     }  
  125.   
  126.     @Override  
  127.     public void showStructure() {  
  128.         if(isEmpty()){  
  129.             System.out.printf("Empty list -1%n");  
  130.             return;  
  131.         }  
  132.         DList tem = head;  
  133.         while (tem.next != null){  
  134.             System.out.printf("%c ",tem.element);  
  135.             tem = tem.next;  
  136.         }  
  137.         cursor = getIntCursor();  
  138.         System.out.printf("%d%n",cursor);  
  139.     }  
  140.   
  141.     private String myRes(){  
  142.         if(isEmpty()){  
  143.             return "Empty list -1";  
  144.         }  
  145.         String myRes = "";  
  146.         DList tem = head;  
  147.         while (tem.next != null){  
  148.             myRes = myRes + " " + tem.element;  
  149.             tem = tem.next;  
  150.         }  
  151.         cursor = getIntCursor();  
  152.         myRes= myRes+" "+tem.element+" "+cursor;  
  153.         return myRes;  
  154.     }  
  155.   
  156.     @Override  
  157.     public String toString() {  
  158.         return myRes();  
  159.     }  
  160.   
  161.     private int getIntCursor(){  
  162.         if (isEmpty()){  
  163.             return -1;  
  164.         }  
  165.         DList tem = head;  
  166.         int intCursor = 0;  
  167.         while (tem != cur){  
  168.             intCursor++;  
  169.             tem = tem.next;  
  170.         }  
  171.         return intCursor;  
  172.     }  
  173. }  

  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.util.Scanner;  
  4.   
  5. public class ReadFile {  
  6.     public static void readFile() throws FileNotFoundException {  
  7. //        List list = new SeqList();  
  8. //        List list = new LList();  
  9.         List list = new DList();  
  10.         Scanner in = new Scanner(new File("src/test.txt"));  
  11.         Scanner in2 = new Scanner(new File("src/list_result.txt"));  
  12.         String row;  
  13.         String ans;  
  14.         String myAns;  
  15.         boolean vertify = true;  
  16.         while (in.hasNextLine() && in2.hasNextLine()){  
  17.             row = in.nextLine();  
  18.             ans = in2.nextLine();  
  19.             int pivot = 0;  
  20.             char tem;  
  21.             while(pivot!=row.length()){  
  22.                 tem = row.charAt(pivot++);  
  23.                 switch (tem){  
  24.                     case '+':  
  25.                         tem = row.charAt(pivot++);  
  26.                         list.insert(tem);  
  27.                         break;  
  28.                     case '-':  
  29.                         list.remove();  
  30.                         break;  
  31.                     case '=':  
  32.                         tem = row.charAt(pivot++);  
  33.                         list.replace(tem);  
  34.                         break;  
  35.                     case '#':  
  36.                         list.gotoBeginning();  
  37.                         break;  
  38.                     case '*':  
  39.                         list.gotoEnd();  
  40.                         break;  
  41.                     case '>':  
  42.                         list.gotoNext();  
  43.                         break;  
  44.                     case '<':  
  45.                         list.gotoPrev();  
  46.                         break;  
  47.                     case '~':  
  48.                         list.clear();  
  49.                         break;  
  50.                     default:  
  51.                         break;  
  52.                 }  
  53.             }  
  54.             myAns = list.toString();  
  55.             System.out.println(myAns.strip());  
  56.             System.out.println(ans.strip());  
  57.             boolean equals = myAns.strip().equals(ans.strip());  
  58.             System.out.println(equals);  
  59.             vertify = vertify&& equals;  
  60.         }  
  61.         System.out.printf("经检验测试结果和标准答案:%b",vertify);  
  62.     }  
  63.   
  64.     public static void main(String[] args) throws FileNotFoundException {  
  65.         readFile();  
  66.     }  
  67. }  

任务二

问题一第一问——递归版

  1. public class PermutationByRecursion {  
  2.     static String str;  
  3.     static char[] res;  
  4. //    初始化函数  
  5.     public static void permutationByRecursion(String s){  
  6.         str = s;  
  7.         res = new char[str.length()];  
  8.         for(int i = 0;i<str.length();i++){  
  9.             res[i] = str.charAt(i);  
  10.         }  
  11.         permutationByRecursion(0, res.length-1);  
  12.     }  
  13.     public static void permutationByRecursion(int left,int right){  
  14.         if (left == right){  
  15.             System.out.print(res);  
  16.             System.out.print(" ");  
  17.         }  
  18.         for(int i=left;i<=right;i++){  
  19.             swap(res,left,i);  
  20. //            进入递归  
  21.             permutationByRecursion(left+1,right);  
  22.             swap(res,left,i);  
  23.         }  
  24.     }  
  25. //交换方法  
  26.     private static void swap(char[] res,int left,int right){  
  27.         char temp = res[left];  
  28.         res[left] = res[right];  
  29.         res[right] = temp;  
  30.     }  
  31. }  

问题一栈版本

  1. public class MyStack {  
  2.     public int[] listArr;  
  3.     private int tail = -1;//不仅是指向尾部的指针,也代表了元素大小  
  4.     public MyStack(){  
  5.         this(10000);  
  6.     }  
  7.     public MyStack(int MAX){  
  8.         listArr = new int[MAX];  
  9.     }  
  10.     public void push(int t){  
  11.         listArr[++tail] = t;  
  12.     }  
  13.     public int pop(){  
  14.         return listArr[tail--];  
  15.     }  
  16.     public boolean isHas(int x){  
  17.         for (int i=0;i<=tail;i++){  
  18.             if (listArr[i]==x){  
  19.                 return true;  
  20.             }  
  21.         }  
  22.         return false;  
  23.     }  
  24.     public boolean isEmpty(){  
  25.         return tail==-1;  
  26.     }  
  27.     public int size(){  
  28.         return tail+1;  
  29.     }  
  30.     public void printAns(String s){  
  31.         for(int i=0;i<=tail;i++){  
  32.             System.out.printf("%c",s.charAt(listArr[i]));  
  33.         }  
  34.         System.out.printf(" ");  
  35.     }  
  36. }  

  1. public static void permutationByNoRecursion(String s){  
  2.     MyStack myStack = new MyStack();  
  3.     for (int i = 0; i < s.length(); i++){  
  4.         myStack.push(i);  
  5.     }  
  6.       从低到高排序输出  
  7.     myStack.printAns(s);  
  8.       开始循环  
  9.     while (!myStack.isEmpty()){  
  10.         int i = myStack.pop() + 1;//加一是为了与其位置匹配  
  11.         while (i < s.length()){  
  12.             if (!myStack.isHas(i)){  
  13.                 myStack.push(i);  
  14.                 //寻找未进栈的元素进栈  
  15.                 for (int j = 0; j < s.length(); j++){  
  16.                     if (!myStack.isHas(j)){  
  17.                         myStack.push(j);  
  18.                     }  
  19.                 }  
  20.                 myStack.printAns(s);  
  21.                 break;  
  22.             }  
  23.             i++;  
  24.         }  
  25.     }  
  26. }

问题二变形一

  1.     public static void permutationByRecursion(int left,int right){  
  2.         if (left == right){  
  3.             System.out.print(res);  
  4.             System.out.print(" ");  
  5.         }  
  6.         for(int i=left;i<=right;i++){  
  7. //            在递归前加入不重复的条件  
  8.             if (res[left]!=res[i]||i==left){  
  9.                 swap(res,left,i);  
  10.                 permutationByRecursion(left+1,right);  
  11.                 swap(res,left,i);  
  12.             }  
  13.         }  
  14.     }

问题二变形二

  1. public class PermutationByRecursion3 {  
  2.     static String str;  
  3.     static char[] strArr;  
  4.     static char[] res;  
  5.     public static void permutationByRecursion(String s,int k){  
  6.         str = s;  
  7.         strArr = new char[str.length()];  
  8.         res = new char[k];  
  9.         for(int i = 0;i<str.length();i++){  
  10.             strArr[i] = str.charAt(i);  
  11.         }  
  12. //        引入不会出现的字符作为区分符号‘ ’  
  13.         for(int i = 0;i<k;i++){  
  14.             res[i] = ' ';  
  15.         }  
  16.         permutationByRecursion(k);  
  17.     }  
  18.     private static void permutationByRecursion(int k){  
  19. //        递归条件  
  20.         if (k==0){  
  21.             System.out.print(res);  
  22.             System.out.print(" ");  
  23.             return;  
  24.         }  
  25. //        把不重复字符加入(十分暴力^_^)  
  26.         for(char strArr:strArr){  
  27.             if (!has(res,strArr)){  
  28.                 res[res.length-k] = strArr;  
  29.             } else{  
  30.                 continue;  
  31.             }  
  32. //            进入递归  
  33.             permutationByRecursion(k-1);  
  34. //            重新置为表示字符  
  35.             res[res.length - k] = ' ';  
  36.         }  
  37.     }  
  38. //    判断字符是否在字符数组内  
  39.     private static boolean has(char[] res,char s){  
  40.         for (char tem:res){  
  41.             if (tem == s){  
  42.                 return true;  
  43.             }  
  44.         }  
  45.         return false;  
  46.     }  
  47. }  

测试类(适用问题一问题二)

  1.     public static void main(String[] args) {  
  2. //        PermutationByRecursion.permutationByRecursion("abcde");  
  3. //        PermutationByRecursion2.permutationByRecursion("abcde");  
  4.         PermutationByRecursion3.permutationByRecursion("abcde",4);  
  5.     } 

任务三

  1. import java.util.NoSuchElementException;  
  2. import java.lang.IllegalArgumentException;  
  3.   
  4. public class ResizingQueue<T> {  
  5. //    成员变量外加初始化  
  6.     private int maxSize = 2;  
  7.     private int front = 0;  
  8.     private int rear = 0;  
  9.     private T[] listArray =(T[]) new Object[2];  
  10. //    无参构造方法  
  11.     public ResizingQueue(){}  
  12. //    入队操作,要注意空队列,满队列等情况  
  13.     public void enqueue(T element) throws IllegalArgumentException{  
  14.         if (element == null){  
  15.             throw new IllegalArgumentException();  
  16.         }  
  17.         if (isExpand()){  
  18. //            expand()方法改变了大小,改变了front,rear,maxSize数值  
  19.             listArray = expand(maxSize,(maxSize-1)*2+1);  
  20.         }  
  21.         rear = (rear+1)%maxSize;  
  22.         listArray[rear] = element;  
  23.     }  
  24.     public T dequeue() throws NoSuchElementException{  
  25.         if (isEmpty()){  
  26.             throw new NoSuchElementException();  
  27.         }  
  28.         listArray[front] = null;  
  29.         front = (front+1)%maxSize;  
  30.         T res =listArray[front];  
  31.         if (isShrink()){  
  32.             listArray = shrink(maxSize,(maxSize-1)/2);  
  33.         }  
  34.         return res;  
  35.     }  
  36.     public int size(){  
  37.         if (rear>=front){  
  38.             return rear-front;  
  39.         }  
  40.         return maxSize-front+rear;  
  41.     }  
  42.     public boolean isFull(){  
  43.         return front == (rear+1)%maxSize;  
  44.     }  
  45.     public boolean isEmpty(){  
  46.         return rear == front;  
  47.     }  
  48.     private boolean isExpand(){  
  49.         return isFull();  
  50.     }  
  51.     private boolean isShrink(){  
  52.         return size()<=(maxSize-1)/4;  
  53.     }  
  54.     private T[] expand(int eSize, int nSize){  
  55.         T[] tem = (T[]) new Object[nSize];  
  56.         int temPivot = 0;  
  57.         if (front>rear){  
  58.             for (int i=front;i<eSize;i++){  
  59.                 tem[temPivot++] = listArray[i];  
  60.             }  
  61.             for (int i=0;i<=rear;i++){  
  62.                 tem[temPivot++] = listArray[i];  
  63.             }  
  64.         }  
  65.         else {  
  66.             for (int i=front;i<=rear;i++){  
  67.                 tem[temPivot++] = listArray[i];  
  68.             }  
  69.         }  
  70.         maxSize = nSize;  
  71.         front = 0;  
  72.         rear = temPivot-1;  
  73.         return tem;  
  74.     }  
  75.     private T[] shrink(int eSize,int nSize){  
  76. //        需要对nSize进行判断  
  77.         nSize = (nSize<=3)?2:nSize;  
  78.         T[] tem = (T[]) new Object[nSize];  
  79.         int temPivot = 0;  
  80.         if (rear>=front){  
  81.             for (int i=front;i<=rear;i++){  
  82.                 tem[temPivot++] = listArray[i];  
  83.             }  
  84.         }  
  85.         else{  
  86.             for (int i=front;i<eSize;i++){  
  87.                 tem[temPivot++] = listArray[i];  
  88.             }  
  89.             for (int i=0;i<=rear;i++){  
  90.                 tem[temPivot++] = listArray[i];  
  91.             }  
  92.         }  
  93.         front = 0;  
  94.         rear = temPivot - 1;  
  95.         maxSize = nSize;  
  96.         return tem;  
  97.     }  
  98.     @Override  
  99.     public String toString() {  
  100.         String res = "";  
  101.         res += "[";  
  102. //        不大于20的情况  
  103.         if (this.size()<=20){  
  104.             if (rear>=front){  
  105.                 for (int i = front+1;i<=rear;i++){  
  106.                     res = res + listArray[i] + " ";  
  107.                 }  
  108.             }  
  109.             else {  
  110.                 for (int i = front+1;i<maxSize;i++){  
  111.                     res = res + listArray[i] + " ";  
  112.                 }  
  113.                 for (int i = 0;i<=rear;i++){  
  114.                     res = res + listArray[i] + " ";  
  115.                 }  
  116.             }  
  117.         }  
  118. //        大于20的情况  
  119.         else {  
  120.             int tem = (front+1)%maxSize;  
  121.             int count = 0;  
  122. //            去前五个元素  
  123.             do{  
  124.                 res = res + listArray[tem] + " ";  
  125.                 tem = (tem+1)%maxSize;  
  126.                 count++;  
  127.             }  
  128.             while (count<5);  
  129.             res += " ... ";  
  130. //            取后五个元素  
  131.             while (count<this.size()-5){  
  132.                 tem = (tem+1)%maxSize;  
  133.                 count++;  
  134.             }  
  135.             while (count<this.size()){  
  136.                 res = res + listArray[tem] + " ";  
  137.                 tem = (tem+1)%maxSize;  
  138.                 count++;  
  139.             }  
  140.         }  
  141.         res = res.strip()+"]";  
  142.         res += "\nelements: " + this.size() + " size:"+(this.maxSize-1);  
  143.         return res;  
  144.     }  
  145. }  

  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.util.Scanner;  
  4.   
  5. public class Test {  
  6.     public static void readFile() throws FileNotFoundException {  
  7. //        Scanner in = new Scanner(new File("src/test1000.txt"));  
  8. //        Scanner in2 = new Scanner(new File("src/result1000.txt")); 
  9.         Scanner in = new Scanner(new File("src/test5000.txt"));  
  10.         Scanner in2 = new Scanner(new File("src/result5000.txt"));  
  11.         ResizingQueue resizingQueue = new ResizingQueue<Integer>();  
  12.         int nextInt = 0;  
  13.         String nextString;  
  14.         boolean vertify = true;  
  15.         while (in.hasNext()){  
  16.             if (in.hasNextInt()){  
  17.                 nextInt = in.nextInt();  
  18. //                System.out.printf("读取到的数字为:%d",nextInt);  
  19.                 resizingQueue.enqueue(nextInt);  
  20.             }  
  21.             else{  
  22.                 nextString = in.next();  
  23.                 char nextChar;  
  24.                 int pivot=0;  
  25.                 while(pivot!=nextString.length()){  
  26.                     nextChar = nextString.charAt(pivot);  
  27.                     pivot++;  
  28.                     if (nextChar == '-'){  
  29.                         resizingQueue.dequeue();  
  30.                     }  
  31.                     if (nextChar == '?'){  
  32.                         System.out.println(resizingQueue.toString());  
  33.                         String tem1 = in2.nextLine();  
  34.                         String tem2 = in2.nextLine();  
  35.                         String res = tem1 + "\n" + tem2;  
  36.                         System.out.println("res为:"+res);  
  37.                         boolean temVertify = res.strip().equals(resizingQueue.toString().strip());  
  38.                         System.out.println("比较结果为:"+temVertify);  
  39.                         vertify = vertify && temVertify;  
  40.                     }  
  41. //                    System.out.printf("当前读取到的符号为:%c%n",nextChar);  
  42.                 }  
  43.             }  
  44. //            System.out.printf("当前队列最大容量:%d,当前队列容量:%d front为:%d rear为:%d%n",resizingQueue.getMaxSize(),resizingQueue.size(),resizingQueue.getFront(),resizingQueue.getRear());  
  45. //            System.out.printf(resizingQueue.toString()+"%n");  
  46.         }  
  47.         System.out.printf("经检验输出的字符串与结果符合判定:%b",vertify);  
  48.     }  
  49.   
  50.     public static void main(String[] args) throws FileNotFoundException {  
  51.         readFile();  
  52.     }  
  53. }  

任务四

  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.util.Scanner;  
  4.   
  5. public class RadixSortInt {  
  6.     static ResizingQueue[] resizingQueue = new ResizingQueue[10];  
  7. //    static int[] arr = {27,91,100,9,17,23,84,28,72,5,67,25};//仅为测试数组  
  8.     static void sort(int[] arr,int len){  
  9. //        初始化队列  
  10.         for(int i=0;i<resizingQueue.length;i++){  
  11.             resizingQueue[i] = new ResizingQueue<Integer>();  
  12.         }  
  13. //        变量+初始化  
  14.         boolean flag = true;  
  15.         int modNum = 0;  
  16.         int tem=0;  
  17. //        一位一位操作,直到最长的数字结束  
  18.         while (flag){  
  19.             modNum++;  
  20. //            入队,将数组数字按照顺序入队  
  21.             for (int i=0;i<len;i++){  
  22.                 int pivot = mod(arr[i],modNum);  
  23.                 tem = Math.max(tem,pivot);  
  24.                 resizingQueue[pivot].enqueue(arr[i]);  
  25.                 flag = (tem != 0);  
  26.             }  
  27.             tem = 0;  
  28.             int arrPivot = 0;  
  29. //            出队,重新给数组赋值  
  30.             for (int i=0;i<resizingQueue.length;i++){  
  31.                 while (!resizingQueue[i].isEmpty()){  
  32.                     arr[arrPivot++] = (int)resizingQueue[i].dequeue();  
  33.                 }  
  34.             }  
  35.         }  
  36. //        输出  
  37.         for (int i=0;i<len;i++){  
  38.             System.out.printf("%d ",arr[i]);  
  39.         }  
  40.     }  
  41.   
  42.     private static int mod(int num,int modNum){  
  43.         for(int i=0;i<modNum-1;i++){  
  44.             num = num/10;  
  45.         }  
  46.         return num%10;  
  47.     }  
  48.     public static void main(String[] args) throws FileNotFoundException {  
  49.         int[] arr = new int[10000];  
  50.         int pivot = 0;  
  51.         Scanner in = new Scanner(new File("src/radixSort1.txt"));  
  52.         while (in.hasNextInt()){  
  53.             int tem = in.nextInt();  
  54.             System.out.println(tem);   
  55.             arr[pivot++] = tem;  
  56.         }  
  57.         pivot--;  
  58.         sort(arr,pivot);  
  59.     }  
  60. }  

  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.util.Scanner;  
  4.   
  5. public class RadixSortString {  
  6.     static ResizingQueue[] resizingQueue = new ResizingQueue[26];  
  7. //    static String[] arr = {"Abc","bde","fad","abd","bef","fdd","abe"};//仅为测试数组  
  8.     static void sort(String[] arr,int len){  
  9. //        初始化队列数组  
  10.         for(int i=0;i<resizingQueue.length;i++){  
  11.             resizingQueue[i] = new ResizingQueue<String>();  
  12.         }  
  13. //        因为等宽,所以取第一个字符串长度作为长度  
  14.         int stringLen = arr[0].length();  
  15.         int tem=0;  
  16.         for (int k=0;k<stringLen;k++){  
  17. //            入队操作  
  18.             for (int i=0;i<len;i++){  
  19.                 int pivot = arr[i].toLowerCase().charAt(stringLen-k-1)-'a';  
  20.                 resizingQueue[pivot].enqueue(arr[i]);  
  21.             }  
  22.             tem = 0;  
  23.             int arrPivot = 0;  
  24. //            出队操作  
  25.             for (int i=0;i<resizingQueue.length;i++){  
  26.                 while (!resizingQueue[i].isEmpty()){  
  27.                     arr[arrPivot++] = (String) resizingQueue[i].dequeue();  
  28.                 }  
  29.             }  
  30.         }  
  31.         for (int i=0;i<len;i++){  
  32.             System.out.printf(arr[i]+" ");  
  33.         }  
  34.     }  
  35.     public static void main(String[] args) throws FileNotFoundException {  
  36.         String[] arr = new String[1000000];  
  37.         int pivot = 0;  
  38.         Scanner in = new Scanner(new File("src/radixSort2.txt"));  
  39.         while (in.hasNext()){  
  40.             arr[pivot++] = in.next();  
  41.         }  
  42.         pivot--;  
  43.         sort(arr,pivot);  
  44.     }  
  45. }  

;