《盘点那些秀你一脸的秒天秒地算法》(1)

本系列坚持格式:1个抖机灵算法+2个较简单但是天秀的算法+1个较难天秀算法。

 

bogo排序

Bogo排序(Bogo-sort),又被称为猴子排序,是一种恶搞排序算法。

将元素随机打乱,然后检查其是否符合排列顺序,若否,则继续进行随机打乱,继续检查结果,直到符合排列顺序。
Bogo排序的最坏时间复杂度为O(∞),一辈子也不能输出排序结果,平均时间复杂度为O(n·n!)。

这让我想到了另一个理论:猴子理论,只要让一只猴子一直敲打计算机,理论上会有一天,它能敲出一本圣经出来,但是这个概率小到宇宙毁灭也很难敲出来。。

真的不知道这个排序应该叫做天才还是垃圾哈哈哈,但是闲的没事的我就把他实现出来了。

public class Main {public static void main(String[] args) {int[] arr = { 9,8,7,6,5,4,3,2,1};System.out.println("排序次数" + bogo(arr));for (int i : arr) {System.out.print(i + " ");}}public static int bogo(int[] arr) {int count = 0;while (!isOrdered(arr)) {shuffle(arr);count++;}return count;}// 判断是否有序方法public static boolean isOrdered(int[] arr) {for (int i = 1; i < arr.length; i++) {if (arr[i - 1] > arr[i]) {return false;}}return true;}// 随机排序方法public static void shuffle(int[] arr) {int temp;for (int i = 0; i < arr.length; i++) {int j = (int) (Math.random() * arr.length);temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}}

9是中国最大的数字嘛,我就把数组长度设为9,结果平均排序次数要60万次,不知道我的运气怎么样哈哈,你们也试试吧?

 

然而,有个看似笑话的方法声称可以用O(n)实现Bogo排序,依照量子理论的平行宇宙解释,使用量子随机性随机地重新排列元素,不同的可能性将在不同的宇宙中展开,总有一种可能猴子得到了正确的顺序,量子计算机找到了这个宇宙后,就开始毁灭其他排序不成功的宇宙,剩下一个观察者可以看到的正确顺序的宇宙。

如果想要迈出这个看似荒诞,但令人无比兴奋的"高效算法"的第一步,请先证明"平行宇宙解释"的正确性。

位运算

关于位运算有很多天秀的技巧,这里举一个例子。

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]输出: 1
示例 2:

输入: [4,1,2,1,2]输出: 4

思路:拿map,set,都不符合要求,那怎么办呢?

我们知道什么数字和自己异或,都等于0.

什么数字和0异或,都等于它本身,

异或又符合交换律

所以全部异或一遍,答案就是那个出现一次的数字。

class Solution {public int singleNumber(int[] nums) {int ans = 0;for(int i :nums)ans ^= i;return ans;}
}

有没有被秒了?

 

打擂台

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

把狂野般的思绪收一收,咱们来看看最优解。

class Solution {public int majorityElement(int[] nums) {int count = 0;//当前答案出现的次数Integer candidate = null;//答案for (int num : nums) {if (count == 0) candidate = num;count += (num == candidate) ? 1 : -1;}return candidate;}
}

我来解释一下策略:记录当前的答案candidate ,记录count。这时,我们遍历了一个新的数字,如果和candidate 一样,我们就让count+1,否则让count-1.如果count变成了0,那candidate 就下擂台,换新的擂主(数字)上,也就是说把candidate 更新成新的数字,count也更新成1.

最后在擂台上的一定是答案。

肯定有人怀疑这个做法的正确性吧?我就来说一下它为啥对?

首先,我们想像一下对最终隐藏答案ans最不利的情况:每个其他数字全部都打擂这个答案ans,那ans的count最后也会剩1,不会被打下来。

正常情况呢?其他数字还会互相打擂,这些数字如此“内耗”,又怎么能斗得过最终答案ans呢?对不对?

 

morris遍历

通常,实现二叉树的前序(preorder)、中序(inorder)、后序(postorder)遍历有两个常用的方法:一是递归(recursive),二是使用栈实现的迭代版本(stack+iterative)。这两种方法都是O(n)的空间复杂度(递归本身占用stack空间或者用户自定义的stack),我分别给出一个例子

递归:

void PreorderTraversal( BinTree BT )
{if(BT==NULL)return ;printf(" %c", BT->Data);PreorderTraversal(BT->Left);PreorderTraversal(BT->Right);
}

非递归:

*p=root;
while(p || !st.empty())
{if(p)//非空{//visit(p);进行操作st.push(p);//入栈p = p->lchild;左} else//空{p = st.top();//取出st.pop();p = p->rchild;//右}
}

为啥这个O(n)的空间就是省不掉呢?因为我们需要空间来记录之前的位置,好在遍历完了可以回到父节点。所以这个空间是必须的!如下图:

比如我们遍历2,想遍历4,这时候我们要保证遍历完4以后,还能回到2,我们好去继续遍历5等等结点,所以必须花空间记录。

 

但是,还就有这么一种算法,能实现空间O(1)的遍历,服不服。

你们可能会问,你刚说的,必须有空间来记录路径,怎么又可以不用空间了呢?

这就是morris遍历,它其实是利用了叶子节点大量的空余空间来实现的,只要把他们利用起来,我们就可以省掉额外空间啦。

我们不说先序中序后序,先说morris遍历的原则:

1、如果没有左孩子,继续遍历右子树,比如:

这个2就没有左孩子,这时直接遍历右孩子即可。

2、如果有左孩子,找到左子树最右节点。

比如上图,6就是2的最右节点。

    1)如果最右节点的右指针为空(说明第一次遇到),把它指向当前节点,当前节点向左继续处理。

修改后:

    2)如果最右节点的右指针不为空(说明它指向之前结点),把右指针设为空,当前节点向右继续处理。

 

这就是morris遍历。

请手动模拟深度至少为4的树的morris遍历来熟悉流程。

下面给出实现:

定义结点:

	public static class Node {public int value;Node left;Node right;public Node(int data) {this.value = data;}}

先序:(完全按规则写就好。)

//打印时机(第一次遇到):发现左子树最右的孩子右指针指向空,或无左子树。public static void morrisPre(Node head) {if (head == null) {return;}Node cur1 = head;Node cur2 = null;while (cur1 != null) {cur2 = cur1.left;if (cur2 != null) {while (cur2.right != null && cur2.right != cur1) {cur2 = cur2.right;}if (cur2.right == null) {cur2.right = cur1;System.out.print(cur1.value + " ");cur1 = cur1.left;continue;} else {cur2.right = null;}} else {System.out.print(cur1.value + " ");}cur1 = cur1.right;}System.out.println();}

morris在发表文章时只写出了中序遍历。而先序遍历只是打印时机不同而已,所以后人改进出了先序遍历。至于后序,是通过打印所有的右边界来实现的:对每个有边界逆序,打印,再逆序回去。注意要原地逆序,否则我们morris遍历的意义也就没有了。

完整代码: 

public class MorrisTraversal {public static void process(Node head) {if(head == null) {return;}// 1//System.out.println(head.value);process(head.left);// 2//System.out.println(head.value);process(head.right);// 3//System.out.println(head.value);}public static class Node {public int value;Node left;Node right;public Node(int data) {this.value = data;}}//打印时机:向右走之前public static void morrisIn(Node head) {if (head == null) {return;}Node cur1 = head;//当前节点Node cur2 = null;//最右while (cur1 != null) {cur2 = cur1.left;//左孩子不为空if (cur2 != null) {while (cur2.right != null && cur2.right != cur1) {cur2 = cur2.right;}//找到最右//右指针为空,指向cur1,cur1向左继续if (cur2.right == null) {cur2.right = cur1;cur1 = cur1.left;continue;} else {cur2.right = null;}//右指针不为空,设为空}System.out.print(cur1.value + " ");cur1 = cur1.right;}System.out.println();}//打印时机(第一次遇到):发现左子树最右的孩子右指针指向空,或无左子树。public static void morrisPre(Node head) {if (head == null) {return;}Node cur1 = head;Node cur2 = null;while (cur1 != null) {cur2 = cur1.left;if (cur2 != null) {while (cur2.right != null && cur2.right != cur1) {cur2 = cur2.right;}if (cur2.right == null) {cur2.right = cur1;System.out.print(cur1.value + " ");cur1 = cur1.left;continue;} else {cur2.right = null;}} else {System.out.print(cur1.value + " ");}cur1 = cur1.right;}System.out.println();}//逆序打印所有右边界public static void morrisPos(Node head) {if (head == null) {return;}Node cur1 = head;Node cur2 = null;while (cur1 != null) {cur2 = cur1.left;if (cur2 != null) {while (cur2.right != null && cur2.right != cur1) {cur2 = cur2.right;}if (cur2.right == null) {cur2.right = cur1;cur1 = cur1.left;continue;} else {cur2.right = null;printEdge(cur1.left);}}cur1 = cur1.right;}printEdge(head);System.out.println();}
//逆序打印public static void printEdge(Node head) {Node tail = reverseEdge(head);Node cur = tail;while (cur != null) {System.out.print(cur.value + " ");cur = cur.right;}reverseEdge(tail);}
//逆序(类似链表逆序)public static Node reverseEdge(Node from) {Node pre = null;Node next = null;while (from != null) {next = from.right;from.right = pre;pre = from;from = next;}return pre;}public static void main(String[] args) {Node head = new Node(4);head.left = new Node(2);head.right = new Node(6);head.left.left = new Node(1);head.left.right = new Node(3);head.right.left = new Node(5);head.right.right = new Node(7);morrisIn(head);morrisPre(head);morrisPos(head);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444702.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

caffe安装篇(一)

caffe我选择使用ubuntu源码安装,所以先执行: sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libboost-all-dev protobuf-compiler libhdf5-serial-dev sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev prot…

caffe2安装篇(三)通过docker安装

用普通的安装方式走了不少弯路,感觉还是用docker方便: 参考的是https://hub.docker.com/r/caffe2ai/caffe2/ Latest docker pull caffe2ai/caffe2 Comes with GPU support, CUDA 8.0, cuDNN 7, all options, and tutorial files. Uses Caffe2 v0.8.1. GPU images (for us…

《盘点那些秀你一脸的秒天秒地算法》(3)

斐波那契之美 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列、因数学家列昂纳多斐波那契&#xff08;Leonardoda Fibonacci&#xff09;以兔子繁殖为例子而引入&#xff0c;故又称为“兔子数列”。 这个数列就是1、1、2、3、5、8、13…

Linux(15)-

Linux下的编程开发

《盘点那些秀你一脸的秒天秒地算法》(4)

防止新手错误的神级代码 #define ture true #define flase false #difine viod void #define mian main #define &#xff1b; ; 以后有新手问题就把这几行代码给他就好啦。 不用额外空间交换两个变量 a 5 b 8 #计算a和b两个点到原点的距离之和&#xff0c;并且赋值给…

Linux(16)-

Vim编辑器的使用

php生成有复杂结构的excel文档

以前都用PHPExcel等工具来生成Excel&#xff0c;但是我们有时候需要非常复杂的样式&#xff0c;比如有合并单元格和拆分单元格&#xff0c;甚至有颜色&#xff0c;行间距之类的&#xff0c;这样做起来很费劲&#xff0c;而且你如果使用插件&#xff0c;你也需要学习这里我们可以…

caffe2安装篇(二) ubuntu16.04 安装方法

caffe2 ubuntu16.04 安装方法 Caffe2的安装相比于caffe在安装的时候更加简便,略去了Makefile.config的各种配置,对于有无GPU以及各种可选库例如opencv,anaconda的支持也更简单。(其实你直接装好库以后make就好,以GPU为例,在make的时候,自动检测你是否安装了CUDA,若没有…

为啥用redis解决会话呢?

什么是会话&#xff1f; 会话可简单理解为&#xff1a;用户开一个浏览器&#xff0c;点击多个超链接&#xff0c;访问服务器多个web资源&#xff0c;然后关闭浏览器&#xff0c;整个过程称之为一个会话。 •会话过程中要解决的一些问题&#xff1f; –每个用户不可避免各自会…

推荐系统(5)-深度推荐模型-AutoRec、DeepCrossing、NeuralCF、PNN、WideDeep、FNN、DeepFM、NFM

GBDTLR1. AutoRec-20152. Deep Crossing-20163. NeuralCF-20164. PNN-20165. Wide&Deep-20166. Deep&Cross-20177.FM深度学习7.1 FNN-20167.2 DeepFM-20177.3 NFM-2017《深度学习/推荐系统》读书笔记2016年开始&#xff0c;推荐系统和计算广告全面进入深度学习时代。 &…

关于在安装caffe2环境中遇到的坑整理(欢迎入坑讨论)

1.ImportError: cannot import name caffe2_pb2 测试caffe2的pytorch环境是否正常的时候使用 root@lxsj-ThinkStation:~/pytorch# python Python 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609] on linux2 Type "help", "copyright", &…

leetcode172. 阶乘后的零 最快算法

给定一个整数 n&#xff0c;返回 n! 结果尾数中零的数量。 示例 1: 输入: 3 输出: 0 解释: 3! 6, 尾数中没有零。 示例 2: 输入: 5 输出: 1 解释: 5! 120, 尾数中有 1 个零. 说明: 你算法的时间复杂度应为 O(log n) 。 思路&#xff1a;102*5&#xff0c;而因数中2一定比…

Win10 连接 Ubuntu16.04.3(通过Xdrp连接xfce4界面)

Win10 连接 Ubuntu16.04.3(通过Xdrp连接xfce4界面) sudo apt-get install xrdp sudo apt-get install vnc4server sudo apt-get install xubuntu-desktop echo "xfce4-session" >~/.xsession sudo apt-get install dconf editor sudo dconf editor(或者在搜索…

Linux(17)-

Make编译机制,Configure

听说你还在纠结自己没访问量?成不了“博客专家”?

一、提高浏览量的技巧 相信很多人都这么想过&#xff1a;“我文章写的这么好&#xff0c;怎么就没人看呢&#xff1f;”&#xff1b; 或者这样想过&#xff1a;“这文章写得明明比我烂很多&#xff0c;凭什么这么多浏览量&#xff1f;”&#xff1b; 虽然在我看来这是极其严…

推荐系统(6)-注意力机制+深度推荐模型、强化学习推荐系统

注意力机制深度推荐模型、强化学习推荐系统1.AFM -20172.DIN-20173.DIEN-20194. DRN-20181.AFM -2017 Attention factorization machines–浙江大学–基于模型结构的改进 引入注意力机制FM&#xff0c; 可视为NFM模型的改进。给特征交叉池化后的特征向量施加不同的注意力权重。…

Caffe安装的坑整理

怎么说了,入了深度学习的坑,就要踩一踩才算你入门,这里我整理了我在安装学习caffe自己遇到的坑: 1.Caffe-GPU编译问题:nvcc fatal : Unsupported gpu architecture compute_20 仔细查看了一下 Makefile.config 中 CUDA_ARCH 设置未按规定设置: # CUDA architecture se…

leetcode74. 搜索二维矩阵 ,你见过吗

编写一个高效的算法来判断 m x n 矩阵中&#xff0c;是否存在一个目标值。该矩阵具有如下特性&#xff1a; 每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。 示例 1: 输入: matrix [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34,…

pytorch学习 入门篇(一)

PyTorch 是什么? 它是一个基于 Python 的科学计算包, 其主要是为了解决两类场景: NumPy 的替代品, 以使用 GPU 的强大加速功能一个深度学习研究平台, 提供最大的灵活性和速度Tensors(张量) Tensors 与 NumPy 的 ndarrays 非常相似, 除此之外还可以在 GPU 上使用张量来加速…

关系数据库——范式/反范式的利弊权衡和建议

范式&#xff08;避免数据冗余和操作异常&#xff09; 函数依赖 A->B A和B是两个属性集&#xff0c;来自同一关系模式&#xff0c;对于同样的A属性值&#xff0c;B属性值也相同 平凡的函数依赖 X->Y&#xff0c;如果Y是X的子集 非平凡的函数依赖 X->Y&#xff…