ARTS 2019 02 02 (16)

ARTS
Algorithm:每周至少做一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip/Tech:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章;

Algorithm

本周做了几道算法题,主要是涉及排序和回溯的思想,发现回溯的思想很有趣:
首先是一道和排序有关的算法题:

969. 煎饼排序

https://leetcode-cn.com/problems/pancake-sorting/
简单来说就是,在这个题目中你,你只能用一种 移动 元素的方法,就是把k之前的元素进行翻转,这个k的值是你自己定的,可以移动多次。
基本上就是这样,其实用冒泡来排序这题很好解答,就是你每次找还没排序里面最大的那个,然后移动到最后面,没排序的的那部分的最后面。其实就是交换的手段复杂了点,首先把找到的元素的移动到首位(就是用找到的元素所在的索引值+1来作为k值),然后再根据未排序的数组的长短给个k值,再次移动;这样,需要移动的未排序的数组里的最大的元素就算移动好了。OK,Show the code:

class Solution {
    public List<Integer> pancakeSort(int[] A) {
        List<Integer> res = new LinkedList<>();
        int length = A.length;
        // 从最后一个开始遍历,找最大的元素
        for (int i = length - 1; i >= 0; i--) {
        	//记录下未排序的里的最大的值的索引
            int maxIndex = i;

            //从i到0都是还没排序的号的数组
            for (int j = i;  j >= 0; j--) {
                if (A[maxIndex] < A[j]) {
                    maxIndex = j;
                }
            }
            if (maxIndex != i) {
                swapPancake(res, A, maxIndex, i);
            }
        }
        return res;
    }

 	/**
     * 用最大那个数组的索引值和需要交换的位置进行两次煎饼排序
     */
    public void swapPancake(List<Integer> list, int[] array, int maxIndex, int i) {
         //如果翻转得值为1就不翻转
        if (maxIndex + 1 != 1)  {
            list.add(maxIndex + 1);
            reverse(array, maxIndex);
        }
        //如果翻转得值为1就不翻转
        if (i + 1 != 1) {
            list.add(i + 1);
            reverse(array, i);
        }
    }

    public void reverse(int[] array, int last) {
        for (int i = 0; i <= last / 2; i++) {
            int temp = array[i];
            array[i] = array[last - i];
            array[last - i] = temp;
        }

    }
}

看了这题的最佳的解答,发现,其实思路是相同的,也是每次去寻找的最大的值,但是那个最好的答案的比较的方法不同。因为题目当中有提示:

1 <= A.length <= 100
A[i] 是 [1, 2, …, A.length] 的排列

所以只要找到那个位置上对应的值就好,不用去进行全部的循环,所以最好的做法是减少了那个寻找未排序的数组中的最大值的操作。这个思路很好。这是核心的代码,相信你只要看懂了我上面的代码,这段代码一样很容易理解:

for (int k = A.length; k > 0; --k) {
 	int i = 0;
    while (k != A[i]) {
        ++i;
    }
    reverse(A, i + 1);
    if (0 != i) {
        ans.add(i + 1);
    }
    reverse(A, k);
    if (1 != k) {
        ans.add(k);
    }
}

51. N皇后

https://leetcode-cn.com/problems/n-queens/

52. N皇后 II

https://leetcode-cn.com/problems/n-queens-ii/
这是经典的题目,几乎做算法必做这些题,需要用到回溯思想,当然,回溯的这个思想有各种不同的实现方式,我个人做了些题目的感觉到,实现回溯的方法不少,你用递归可以实现,用深度优先搜索也是可以实现的。
因为用递归的实现,其中必然涉及循环里面的出现递归,看起来会很复杂,看起来会很难理解,但是这个毕竟经典,就算是硬学也是肯定更要学会的,在看代码之前,需要理解关于N皇后的几个要点,N皇后的问题中经典的就是八皇后的问题一下我们就以八皇后的作为例子:
(1)基本上因为需要一个数组来保存的皇后的位置情况,一般用一个一维数组,索引代表行数,每个索引对应的值代表列数

int[] array = new int[8];
//行数
int i = 0;
//列数
array[i] = 0;
//具体到棋盘就是下面的情况。
//Q * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 
//* * * * * * * * 

(2)递归条件:判断点所在的位置是否满足皇后不互相攻击的规则,如果满足就进入下一次判断的递归中。
(3)停止条件:判断我们是否已经走到了最后一行,如果到了,就可以记录我们知道了一个解。
(4)循环条件:如果列的条件不满足我们的皇后的不攻击的规则,那么就进入下一行判断。
以上可能你现在还没明白啥意思,没事,show you code:

List<List<String>> resultList = new LinkedList<List<String>>();
public List<List<String>> solveNQueens(int n) {
    int[] res = new int[n];
    getQueen(0, n, res);
    return resultList;
}
public void getQueen(int row, int length, int[] array) {
	//判断我们是否已经走到了最后一行,如果到了,就可以记录我们知道了一个解。
    if (row == length) {
        setList(array);
    }
    for (int i = 0; i < length; i++) {
    	//如果列的条件不满足我们的皇后的不攻击的规则,就直接进行下一列的判断。
        if (!canFight(array, row, i)) {
            array[row] = i;
            //如果满足就进入下一次判断的递归中
            getQueen(row + 1, length, array);
        }
    }
}
//判断是否满足互相攻击原则
public boolean canFight(int[] array, int row, int column) {
    for (int i = row - 1; i >= 0; i--) {
        if (array[i] == column) { // 是否同列
            return true;
        }
        if (Math.abs(row - i) == Math.abs(column - array[i])) {//是否在一条斜线上
            return true;
        }
    }
    return false;
}
public void setList(int[] array) {
    int len = array.length;
    List<String> list = new LinkedList<>();
    for (int i = 0; i < len; i++) {
        String temp = "";
        for (int j = 0; j < len; j++) {
            if (j == array[i]) {
                temp += "Q";
            } else {
                temp += ".";
            }
        }
        list.add(temp);
    }
    resultList.add(list);
}

因为要用户递归来实现回溯思想,就要比单纯的递归多出一个循环条件,以及找到一个正确的存储当前的数据状态的数据结构(一般是用数组)。
希望这些你们可以看懂!!!

Review

https://dzone.com/articles/introduction-to-java-bytecode
关于Java 字节码的简单的介绍,作者写的很浅显易懂,先从字节文件的数据类型入手,然后结合JVM的几种架构,结合字节码从而讲解了java程序每次都是怎么跑的。

Tip/Tech

回溯递归的一种实现,以及实现了广度优先搜索和深度优先搜索。

Share

https://en.wikipedia.org/wiki/Problem-based_learning
问题导向学习法
这个是一种大学里面提倡的新型的学习方法。我们现在的大学的学习的流程类似于,学习课本,然后进行一些相关的问题的训练,然后考试核查是否通过。其实这样的弊端也是很明显的,如果不是那么聪明的同学会因为没有实践,导致和现实有些脱离。
基于问题导向的学习就重点在于引导学生去解决在不同的场景中会遇到的各种问题,让学生自己去寻找各种合适的答案,非常训练学生的自学,以及搜集信息的能力,独立思考的能力。
老师负责引导学生的思考,引申出各种思考的方向。
学生则自己去进行各种研究。
这个其实说起来还挺多能说,感觉可以单独写一篇了。有时间单独写一篇来进行讨论下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值