Java考研试题之数据结构解法

开发 后端
本文讲述09年Java考研试题之数据结构解法。文中描述了两种解法,分别基于递归以及消除递归。

今天去网上看了一下09年的Java考研试题,看见该题目(图片):

Java考研试题

先来定义结点(为了简便,省略set/get):

  1. public class Node  
  2. {  
  3.  public int data;  
  4.  public Node link;  
  5. }  

对于这个Java考研试题,我能想到的两种解法,一个基于递归:

递归版的思路就是,基于当前结点,如果后一个是倒数第K-1,那么当前结点是所求,若不然,返回当前是倒数第几个。

  1. public int printRKWithRecur(Node head,int k)  
  2.     {  
  3.         if(k==0||head==null||head.link==null)return 0;  
  4.         if(_recurFind(head.link,k)>=k)return 1;  
  5.         return 0;  
  6.     }  
  7.     private final int _recurFind(Node node, int k) {  
  8.         if(node.link==null)  
  9.         {  
  10.             return 1;  
  11.         }  
  12.         int sRet=_recurFind(node.link,k);  
  13.         if(sRet==k-1)  
  14.         {  
  15.             System.out.println("Got:"+node.data);  
  16.             return k;  
  17.         }  
  18.         return sRet+1;  
  19.     } 

对每个结点,该算法都只访问一次,因此复杂度O(N)。

第二解法,相对递归来说,这种方法可以算是消除递归版,而且从某种意义上来说比递归更高效,跟省空间,递归版实际上是把回溯的数据存在栈上,而版方法是自己存储,且利用数组实现一个循环队列,只存储K个元素。

  1. public static class CycleIntQueue  
  2.     {  
  3.         int[] datas;  
  4.         int top=0;  
  5.         int num=0;  
  6.         public CycleIntQueue(int n)  
  7.         {  
  8.             datas=new int[n];  
  9.         }  
  10.           
  11.         public void push(int i)  
  12.         {  
  13.             datas[(top++)%datas.length]=i;  
  14.             num++;  
  15.               
  16.         }  
  17.         public int numPushed()  
  18.         {  
  19.             return num;  
  20.         }  
  21.           
  22.           
  23.         public int getButtom()  
  24.         {  
  25.             return datas[top%datas.length];  
  26.         }  
  27.     }  
  28.     public int printRKWithCycleQueue(Node head,int k)  
  29.     {  
  30.         if(k==0||head==null)return 0;  
  31.         CycleIntQueue queue=new CycleIntQueue(k);  
  32.         Node cur=head.link;  
  33.         while(cur!=null)  
  34.         {  
  35.             queue.push(cur.data);  
  36.             cur=cur.link;  
  37.         }  
  38.         if(queue.numPushed()return 0;  
  39.           
  40.         System.out.println("Got:"+queue.getButtom());  
  41.         return 1;  
  42.     } 

本算法,都每个结点也只放一次,另外进行一次入队操作,该操作复杂度O(1),从而,整个算法复杂度仍是O(N).

对于此Java考研试题还有另外一种算法,该算法的空间复杂度为O(1),时间复杂度为O(n)。这在空间复杂度和时间复杂度上应该是比较优化了。

本算法的基本思想如下:既然是查找倒数第K个结点(注意,不是正数,否则就没什么可讨论的了),而且链表是单向的,还不能改变表结构,这就意味着只能从前往后扫描结点。我们首先要知道这个链表有多少个结点(如果总结点数都不知道,何谈倒数?),这个非常简单,只要从头扫描一下链表,再计一下数即可。

在同一时间从事多项工作会大大提升效率,当然,扫描链表也不例外,在扫描链表的同时,还需要做一些其他的工作。既然只能从前向后扫描链表,而且要求倒数第K个结点,那就让我们把这个链表按长度为K分成若干块,而最后扫描的结果要么结点数是K的整数倍(模为0),要么余数(模)不为0(多出几个结点,多出的结点数小于K)。

先看看第二种情况。

假设有12个结点的链表,每一个结点的值从前往后分别是1至12,如下所示:

1  2  3  4  5  6  7  8  9  10  11 12

假设我们要求倒数第5个结点,我们直接就可以看出结果是8.那么用程序如何处理呢?

先按长度为5将上面的结点分成三个区域,如下:

1 2 3 4 5   6 7 8 9 10   11 12

注意,不是物理分,而是使用变量来保存区域的边界(也就是区域最后一个结点的对象)。

从上面的分隔可以看出,最后剩下两个结点,既然是求倒数第5个,而最后剩下了两个,那么还缺5-2=3个,因此,只需要从倒数第二个块(6 7 8 9 10)略过前两个,第三个结点(8)就是我们要求的结果,而5就是题中的k,2就是结点数与k的模,因此,可以推出一个公式,倒数第k个结点就是按长度为k按分成的若干块中的第二块的第(结点数 % k+ 1)个结点。

下面来看看(结点数 % k)为0的情况。假设上面的例子中的k为4,正确的输出结果应为9,分块如下:

1 2 3 4  5 6 7 8  9 10 11 12

从上面的三个块可以看出,结果正好是最后一个块的第一个结点,这时mod为0(mod=结点数 % k),因此,在这种情况也可以使用上面的公式,只是变成了最后一个块。

根据上面的基本思想可以设两个指针,p1和p2,其中p1最终指向倒数第2个完整块,p2最终指向倒数第1个完整块,对于第一种情况,p1指向5,p2指向10,这时可以使p1向后移动(k - mod)个结点即可(从5移动3个正好是8)。而对于第二种情况,p1指向8,p2指向12,而mod=0,这时的结果仍然是mod+1,也就是p1向后移动1个结点就是所求的结果。 为了满足(k=结点数)的情况,需要将p1的初始值设为头结点,这样如果(k=结点数),就直接从头结点向后移动一个结点就是最后的结果,如上面的例子求倒数第12个结点,也就是求正数第1个结点。

下面是这个算法的具体实现(包括核心算法、生成链表及调用核心算法的代码):

  1. public class Test  
  2. {  
  3.     static class Node  
  4.     {  
  5.         public int data;  
  6.         public Node nextNode;  
  7.     }  
  8.     //////////////////////////////////////////  
  9.     //  核心算法  
  10.     private static int findNode(Node headNode, int k)  
  11.     {  
  12.         Node p = headNode, p1 = headNode, p2 = null;   
  13.         int count = 0;  //  表示结点数  
  14.         while (p.nextNode != null)  
  15.         {  
  16.             p = p.nextNode;  
  17.             count++;  
  18.             //  遇到k的整数位个结点,进行分块  
  19.             if (count % k == 0)   
  20.             {                  
  21.                 if (p2 != null)  
  22.                     p1 = p2;  
  23.                 p2 = p;  
  24.             }  
  25.         }  
  26.         //  k超过链表结点数,未找到,返回0  
  27.         // 此处也可以用k > count来判断  
  28.         if (p2 == null)    
  29.         {  
  30.             return 0;  
  31.         }  
  32.         else 
  33.         {  
  34.             int mod = count % k;  
  35.             int offset = mod + 1;  // 任何情况下,最终结果都是p1指向的结点向后移动(mod + 1)个结点  
  36.             for (int i = 0; i < offset; i++)  
  37.                 p1 = p1.nextNode;  
  38.             System.out.println(p1.data);  
  39.             return 1;  
  40.         }  
  41.     }  
  42.     ////////////////////////////////////////  
  43.     public static void main(String[] args) throws Exception  
  44.     {  
  45.         //产生一个包含1个头结点和120个结点的链表  
  46.         Node headNode = new Node();  
  47.         Node p = headNode;  
  48.         for (int i = 0; i < 120; i++)  
  49.         {  
  50.             p.nextNode = new Node();  
  51.             p.nextNode.data = i + 1;  
  52.             p = p.nextNode;  
  53.         }  
  54.         p.nextNode = null;  
  55.         //  开始查找倒数第k个结点,如果找到,返回1,并输出结点的data值  
  56.         System.out.println(findNode(headNode, 12));  
  57.     }  

上面程序的输出结果如下:

109
1

【编辑推荐】

  1. Java笔试题:Java基础概念(附答案)
  2. Java中消除实现继承和面向接口编程
  3. Red Hat CEO呼吁甲骨文继续保持Java开放
  4. 自学Javabean迅速成为Java高手
  5. Java通过JNI调用C语言的方法
责任编辑:王观 来源: 中国IT实验室
相关推荐

2021-07-16 07:57:34

Python数据结构

2022-09-26 07:56:53

AVL算法二叉树

2022-09-21 07:57:33

二叉搜索树排序二叉树

2021-07-13 07:52:03

Python数据结构

2017-03-01 13:58:46

Python数据结构链表

2023-03-28 07:44:23

数据结构数组

2021-07-15 06:43:12

Python数据结构

2021-07-11 12:06:43

python数据结构

2012-02-02 10:21:05

单链表nexthead

2018-06-06 08:54:23

数据结构存储

2012-05-16 17:05:33

Java数据结构

2011-03-31 15:41:51

Cacti数据表结构

2020-12-31 05:31:01

数据结构算法

2021-05-10 07:08:41

数据结构缓存

2023-10-31 08:51:25

数据结构存储数据

2020-10-30 09:56:59

Trie树之美

2009-08-12 18:35:17

C#数据结构

2021-08-29 07:41:48

数据HashMap底层

2022-01-18 19:13:52

背包问题数据结构算法

2012-04-28 14:21:47

Java数据结构线性结构
点赞
收藏

51CTO技术栈公众号