【一起学数据结构与算法分析】第六篇:第一章完整习题

关于第一章学习已经介绍完了。

  1. 【一起学数据结构与算法分析】第一篇:取第K大数的两种方法比较
  2. 【一起学数据结构与算法分析】第二篇:字谜游戏
  3. 【一起学数据结构与算法分析】第三篇:递归的4条设计原则
  4. 【一起学数据结构与算法分析】第四篇:泛型漫谈
  5. 【一起学数据结构与算法分析】第五篇:处理include标签

接下来我们就尝试来解一解书本上的习题。

同学们,上阔~
在这里插入图片描述

1.1 编写一个程序解决选择问题。令k = N / 2 。画出表格显示程序对于N种不同的值的运行时间。

参见上面第一个链接,就不再赘述了,但是按题目要求要补充运行时间(k = N / 2),N大家酌量取值。

N (冒泡)N(k排)运行时间(ms)
1000271
1000051856
100000553407469

1.2 编写一个程序求解字谜问题。

参见上面第二个链接

1.3 只使用处理I/O的printDigit方法,编写一种方法以输出任意Double型量(可以是负的)。

这个问题其实可以分四步解决:

  • 变号
    即把负数变正数并输出负号,就不知道题意说只使用printDigit方法,那么print("-")方法能否使用,如果不能用就不输入吧。
  • 输入整数部分
    递归实现,可参数书本printOut方法的实现。
  • 输入小数点
    没什么好讲的。
  • 输出小数点后面的数
    这地方尤其注意精度问题,如:
print(10.103 - 10) // output 0.10299999999999976 rather than 0.103.

那么想求后面的1可以这样:把10.103乘以10得101.03,把10.103取整再乘以10为100,再把101.03 - 100得到的结果再取整,就是1了,后面递归类推直到相减的结果为0为止,这样就避免了原来的double值在中间计算时损失精度,看网上什么+0.5眼花缭乱的。

奉上完整代码:

@Test
    fun testPrintDouble() {
		printDouble(-0.10839)
    }

    /**
     * Print every digit of a double value by method [printDigit].
     */
    private fun printDouble(value: Double) {
        var positiveValue = value
        // Print and change sign if needed.
        if(value < 0){
            print("-")
            positiveValue = -value
        }
        // Print int part recursively.
        val intValue = value.toInt()
        printInt(intValue)
        print(".")
        // Print double part recursively.
        printPositiveDouble(intValue, positiveValue)
    }

    private fun printInt(value: Int) {
        if(value >= 10) {
            printInt(value / 10)
        }
        printDigit(value % 10)
    }

    private fun printPositiveDouble(intValue: Int, value: Double) {
       if(intValue - value != 0.0) {
           val value10Times = value * 10
           print((value10Times - intValue * 10).toInt())
           printPositiveDouble(value10Times.toInt(), value10Times)
       }
    }

    private fun printDigit(number: Int) {
        print(number)
    }

在这里插入图片描述

1.4 处理include标签

见上面第五个链接。

1.5 编写一种递归方法,它返回数N的二进制表示中1的个数。利用这样的事实:如果N是奇数,那么其1的个数等于N/2的二进制表示中1的个数加1。

如果知道实数的二进制存储,这个问题就很简单了。
比如整数17的二进制为:
0000 0000 0000 0000 0000 0000 0001 0001
整数8的二进制为:
0000 0000 0000 0000 0000 0000 0000 1000
那么我们将整数的二进制右移1位再与1做位与操作就可以了。其中,递归的基点应是value = 0。

/**
     * Output the count of "1" from N's binary.
     * @param value is the number to be calculated.
     * @param sum of "1" performed by bit.
     */
    private fun getOneCount(value: Int): Int {
        if(value != 0) {
            return (value and 1) + getOneCount(value shr 1)
        } else {
            return 0
        }
    }

我们分别把17和8代入,发现17中1的个数为2,8中1的个数为1,确实如题干所示。
辣么,我们再回头想想为什么N比N/2的个数少1呢?
因为N是奇数的话,低位肯定是1,而N/2是N的二进制右移一位,正好把低位的1移走了,所以少了1个。

1.6 编写带有下列声明的例程: public void permute(String str) ; private void permute(char[], int low, int high); 第一个例程是个驱动程序,它调用第二个例程并显示String str中的字符的所有排列。如果str是"abc",那么它输出的串是abc,acb,bac,bca,cab和cba。第二个例程使用递归。

其实就是数学上排列组件问题,那怎么用递归的方法求呢?在文章开头的第三个链接中我们学习了递归的几个原则,其中最重要的是找到递推的规则和基点。

首先我们找下规则:

一个字母:不用处理,直接输出;
两个字母:比如a和b,排列组件为ab和ba
三个字母:如题干所示。
……
所以很容(ma)易(fan)地找到这个规律和基准:

依次把数列中每个元素都放在第一个位置,然后把第二个元素到最后元素构成的新数列再按上述处理,直到新数列中只有一个元素为止。

语言描述还是太啰嗦,我们简单地的画个图:

在这里插入图片描述
上图中红色均为已被固定的元素。

/**
     * Permute array from range of [low] to [high] recursively.
     * @param array to be permuted.
     * @param low is the start of permute range.
     * @param high is the end of permute range.
     */
    private fun permute(array: CharArray, low: Int, high: Int) {
        if(low == high) {
            println(array.contentToString())
            return
        }
        for(i in low .. high) {
            swap(array, low, i)
            // Permute from the next position.
            permute(array, low + 1, high)
            // Restore array to the original against previous swap.
            swap(array, i, low)
        }
    }

完整代码请参考git中ArrangeCharsTest

1.7-1.12

请参见习题资料,不再赘述。

1.13 设计一个泛型类Collection,它存储Object对象的集合(在数组中),以及该集合的当前大小。提供public方法isEmpty、makeEmpy、insert、remove和isPresent。方法isPresent(x)当且仅当在该集合中存在(由equals定义)等于x的一个Object时返回true。

其实就是对Collections的ADT的定义,老朽这边先写个简单的,没有考虑优化、并发等方面,完整版大家可以直接看官方List等实现。

 /**
     * Array contains data with type Any.
     */
    private var elements: Array<Any?> = arrayOfNulls<Any>(INIT_SIZE)

    /**
     * Actual number of elements in collection.
     */
    private var size = 0

    /**
     * Check if collection is empty or not.
     */
    fun isEmpty(): Boolean{
        return size == 0
    }

    /**
     * Clear all elements from collection.
     */
    fun makeEmpty() {
        for(i in 0 until size) {
            elements[i] = null
        }
        size = 0
    }

    /**
     * Insert element to collection.Note that expand array if its space not enough.
     */
    fun insert(element: T?){
        if(size >= elements.size) {
            elements = elements.copyOf(size + INCREMENT_SPACE)
        }
        elements[size] = element
        size ++
    }

    /**
     * Remove element from collection even having multiple same values.
     */
    fun remove(element: T?) {
        if(isEmpty()) {
            return
        }
        // Create a new array to receive the values not equals the removing element.
        val newArray = arrayOfNulls<Any?>(elements.size)
        var pointer = 0
        elements.forEach {
            if(it != element) {
                newArray[pointer ++ ] = it
            }
        }
        // Clear unnecessary references.
        makeEmpty()
        elements = newArray
        size = pointer + 1
    }

    /**
     * Detect whether collection contains the assigned element.
     * @param element to be checked.
     * @return true if collection contains, otherwise not.
     */
    fun isPresent(element: T?): Boolean {
        elements.forEach {
            // Different from java or other language.
            if(element == it) {
                return true
            }
        }
        return false
    }

参见:CollectionTest

1.14 设计一个泛型类OrderedCollection,它存储Comparable的对象的集合(在数组中),以及该集合的当前大小。提供public方法isEmpty、makeEmpty、insert、remove、findMin和findMax。findMin和findMax分别返回该集合中最小的和最大的Comparable对象的引用(如果该集合为空,则返回null)。

首先写一个比较的接口:

interface Comparable<T> {
    fun compareTo(value: T?): Int
}

然后把泛型改下:

class MyOrderedCollection<T> where T: Comparable<T>

最后加几个方法:

    /**
     * Return the min value inside of collection, return null if collection empty.
     */
    fun findMin(): T? {
        if(isEmpty()) {
            return null
        }
        var minElement: T? = null
        elements.forEach {
            if(it == null) {
                minElement = null
            } else if(it.compareTo(minElement) < 0){
                minElement = it as T
            }
        }
        return minElement
    }

参见:OrderedCollectionTest

1.15 定义一个Rectangle类,该类提供getLength和getWidth方法。利用图1-18中的findMax例程编写一种main方法,该方法创建一个Rectangle数组并首先找出依面积最大的Rectangle对象,然后找出依周长最大的Rectangle对象。

定义一个Rectangle:

/**
 * Rectangle with only length and width.
 */
data class Rectangle(private val length: Int, private val width: Int) {

    fun getLength(): Int = length

    fun getWidth(): Int = width
}

定义一个比较接口:

/**
 * An interface for comparing two values.
 */
interface Comparator<T> {
    fun compare(lhs: T, rhs: T): Int
}

分别定义面积比较和周长比较:

/**
 * Compare two rectangles with area.
 */
class AreaComparator: Comparator<Rectangle> {
    override fun compare(lhs: Rectangle, rhs: Rectangle): Int {
        return lhs.getLength() * lhs.getWidth() - rhs.getLength() * rhs.getWidth()
    }
}

/**
 * Compare two rectangles with circumstance.
 */
class CircumstanceComparator: Comparator<Rectangle> {
    override fun compare(lhs: Rectangle, rhs: Rectangle): Int {
        return (lhs.getLength() + lhs.getWidth()) - (rhs.getLength() + rhs.getWidth())
    }
}

使用书上的findMax例程:

/**
     * Find the max from array with rule in comparator.
     */
    fun findMax(array: Array<Rectangle>, cmp: Comparator<Rectangle>): Rectangle? {
        if(array.isEmpty()) {
            return null
        }
        var maxIndex = 0
        for(i in array.indices) {
            if(cmp.compare(array[i], array[maxIndex]) > 0) {
                maxIndex = i
            }
        }
        return array[maxIndex]
    }

.再定义main方法:

@Test
    fun main() {
        val array = arrayOf(
            Rectangle(3, 5),
            Rectangle(6, 2),
            Rectangle(9, 1)
        )
        println("max area:" + findMax(array, AreaComparator()))
        println("max circumstance:" + findMax(array, CircumstanceComparator()))
    }

参见:FindRectangleTest

好了,第一章的习题部分就讲完了,水平有限,有错误的地方可以直接在评论区留言。

码字不易,觉得不错就点个赞加个star吧。
在这里插入图片描述

GIT:https://github.com/codersth/dsa-study

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章代沫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值