自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

rhwayfun专栏

在等待的日子里,刻苦读书,谦卑做人,养得深根,日后才能枝叶茂盛!公众号:技术视点

  • 博客(239)
  • 收藏
  • 关注

原创 二叉树遍历算法之一:前序遍历

递归实现前序遍历二叉树的前序遍历是指从根节点出发,按照先根节点,再左子树,后右子树的方法遍历二叉树中的所有节点,使得每个节点都被访问一次。当调用遍历算法的时候前序遍历的具体过程如下:首先访问根节点,如果根节点不为空,执行输出语句,打印根节点的值。如果左子树不为空,则访问根节点的左孩子,并输出根节点做孩子的值继续访问根节点的左孩子的左孩子,如果不为空则继续输出该左孩子的值;如果这时左孩子为空,

2015-12-07 20:14:26 1746

原创 剑指offer系列之十八:顺时针打印矩阵

题目描述输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.由于每打印完一圈都会改变其起始坐标,所以需要先确定矩阵大小与这个起始坐标的关系,比如一个4阶矩阵,第一圈的起始坐标是(0,0),第二圈

2015-12-07 17:11:54 463

原创 剑指offer系列之十七:二叉树的镜像

题目描述操作给定的二叉树,将其变换为源二叉树的镜像。 输入描述:二叉树的镜像定义:源二叉树 8 / \ 6 10 / \ / \ 5 7 9 11 镜像二叉树 8 / \ 10 6

2015-12-07 16:57:31 421

原创 剑指offer系列之十六:树的子结构

题目描述输入两颗二叉树A,B,判断B是不是A的子结构。这实际上二叉树遍历算法的一种应用,要在原二叉树中查找是否具有某课子树,只需要判断每个节点是否都在二叉树中是否出现即可。所以需要先判断头结点,只有头结点符合要求才继续比较其子树是否符合,一样依次从头结点开始比较直到其左右子树进行比较,如果都符合则说明B是A的子结构。下面是基于这种思路实现的代码(已被牛客AC):

2015-12-07 16:45:50 568

原创 剑指offer系列之十五:合并两个排序的链表

题目描述输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。这道题我的第一思路这样的:可以先遍历这两个排序的链表,把遍历的结果存放在一个集合中,然后调用库函数Arrays.sort方法完成排序,之后,根据这些排好序的结果重新创建一个链表,即为合并之后但仍然排序的链表。但是这种思路需要额外的List和创建链表的空间开销,而且时间复杂度最快也是O(nlogn)O(

2015-12-07 16:40:08 734 2

原创 二分查找算法及其变种

前言二分查找算法也称为折半查找算法,是一种在查找算法中普遍使用的算法。其算法的基本思想是:在有序表中,取中间的记录作为比较关键字,若给定值与中间记录的关键字相等,则查找成功;若给定的值小于中间记录的关键字,则在中间记录的左半区间继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区间继续查找;不断重复这个过程,直到查找成功。否则查找失败。这个思想与孔子中的中庸思想和相似。二分查找算法的实现基于

2015-12-06 16:43:23 4344

原创 剑指offer系列之十四:反转链表

题目描述输入一个链表,反转链表后,输出链表的所有元素。思路如下:在遍历链表上的每个节点的时候,就修改其指针,当遍历到最后一个结点的时候,整个链表就反转完成了。所以需要创建三个变量:一个是当前遍历的结点,一个是遍历结点的前一个结点,还有一个是当前遍历结点的下一个结点。基于这种思路可以写出如下的实现代码:

2015-12-06 15:31:59 661

原创 剑指offer系列之十三:链表中的倒数第k个节点

题目描述输入一个链表,输出该链表中倒数第k个结点。举一个简单的例子,比如链表{1,2,3,4,5},如果要返回倒数第二个节点,也就是k=2,就相当于正数第5-k+1=4个节点,所以我们可以采用两次循环:一次循环得到链表的结点个数;另一次循环则是从链表中找到第n-k+1个节点。虽然是两次循环,但时间复杂度是O(n)O(n),需要注意的是,这里仍然需要对链表的边界条件进行判断。基于这种思路写出如下代码:

2015-12-06 15:26:29 557

原创 剑指offer系列之十二:调整数组顺序使奇数位于偶数前面

题目描述输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。这道题第一思路自然是这样的:从头开始遍历这个数组,如果遇到偶数就把这个数之后的所有数往前移动一位,这样数组会留出一个空位,移动完毕之后把该偶数放到该空位即可。这样处理的话需要两次循环,一次是遍历,另一次是移位,所以时间复杂

2015-12-06 14:30:27 554

原创 剑指offer系列之十一:数值的整数次方

题目描述给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。首先,我觉得这道题思路应该很简单,幂的情况无非是三种:正数、0和负数。当幂是0的时候,直接返回1;当幂是负数的时候,需要先把其转化为正数来处理,然后返回其倒数就可以了;当幂是正数的时候,按照正常的计算方法就可以。实际上这道题主要考察时代码的健壮性——就是对幂的情况的考虑是否周全。下面

2015-12-06 14:18:29 442

原创 常用内部排序算法之五:希尔排序

前言在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的。而这里要讲的希尔排序则是优于直接插入排序的。而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)O(n^2)的神话。这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的。希尔排序的原理由于希尔排序是在直接插入排序的基础

2015-12-05 19:07:07 817

原创 剑指offer系列之十:二进制中1的个数

题目描述:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。比如输入9,9的二进制表示是1001,1的个数是2,所以输出2。这有一个重要结论:一个数与该数减一的结果进行与运算,会把该数右边(低位)第一个1变为0,而该位左边保持不变(高位)。可以举一个简单的例子进行证明:比如1100(对应十进制是12),减去1之后的结果是1011(也就是十进制的11),两个数进行与运算之后,我们发现最后

2015-12-05 15:35:54 455

原创 剑指offer系列之九:矩形覆盖问题

题目描述我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?我们采用从能够简单到复杂的思路思考这个问题,当n=1的时候,只有一个2*1的矩形,所以只有一种方法,记为f(1)=1;当n=2的时候,是两个2*1的矩形,这时候具有两种方式去覆盖这个矩形了(这时候应该是一个正方形),一种是竖着放,一种是横着放,所以有两种方法,记为f

2015-12-05 15:22:44 1383

原创 剑指offer系列之八:跳台阶问题

题目描述一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。这种问题的思路一般是采用数学归纳法,当n=1时,只有一种跳法;当n=2时,青蛙可以一次跳2级,也可以一次跳两级,需要跳两次,所以有两种跳法;当n=3的时候,首先考虑一次跳1级的情况,由于有3级,所以还有2级没跳,而跳两级的跳法已经在前面n=2的情况中计算得到了;下面考虑一次跳2级的情况,同样跳完2级的之

2015-12-05 15:08:02 1308

原创 Java并发编程系列之二:线程状态

线程的状态一共有6种,在任意时刻线程的状态只能是其中的一种。正确理解线程的状态有助于我们更容易理解线程。具体的线程状态如下: 状态名称 说明 NEW 初始状态,线程被构建,但是还没有调用start方法 RUNNING 运行状态 BLOCKED 阻塞状态,表示线程阻塞于锁 WAITING 等待状态,表示线程线程进入等待状态,进入该状态后需要其他线程做出通知动作

2015-12-04 22:01:21 1264 2

原创 剑指offer系列之七:斐波那契数列

题目描述大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。这题比较简单,直接AC代码如下:package com.rhwayfun.offer;public class Fibonacci { public int getN(int n){ if(n == 0){ return 0; }else if(n ==

2015-12-04 14:41:40 534 2

原创 剑指offer系列之五:用两个栈实现队列

题目描述用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。栈的特点是先进后出,而队列的特点是先进先出。题目中提到使用两个栈实现队列,好像有戏。现在问题是如何把栈的出栈和入栈与队列的入队和出队联系起来?因为现在只有栈,所以在实现的队列中,只能先往栈中添加元素,这点比较好理解;那么出队呢,由于先进去的元素被压在栈底,而如果是队列的话,必须是栈底的那个元素先出队。现在可以

2015-12-04 14:26:39 468

原创 剑指offer系列之六:旋转数组的最小值

题目描述把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。这题其实不用考虑旋转数组的特性,采用顺序查找的方式也能很快写出实现代码。代码如下:public int orderSearch(int[] a){ if(a == null

2015-12-04 14:24:03 516 1

原创 剑指offer系列之四:重建二叉树

题目描述输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。在完成代码之前,我自己分析了一下如何根据前序遍历和中序遍历的结果构建一棵二叉树。首先,根据二叉树遍历的性质,由前序遍历的结果序列可知该二叉树的根节点是1,在根

2015-12-03 17:14:41 575

原创 剑指offer系列之三:在二维数组中查找元素

题目描述:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。由于题目条件的成立,所以使得这道题可以使用对角线的方法完成,可以从右上角的元素考虑,如果目标查找元素小于右上角的元素,那么不可能在右上角元素所在的列,如果目标大于剩余列的右上角的元素,那么不可能在该右上角元素所在的行。依照这个

2015-12-03 16:00:43 827

原创 常用内部排序算法之四:简单选择排序、直接插入排序和冒泡排序

前言之所以把这三类算法放在一块,是因为除此之外的算法都是在这三类算法的基础上进行优化的。简单选择排序的思想是每一趟n−i+1(i=1,2,...,n−1)n-i+1(i=1,2,...,n-1)个记录中选择最小的记录作为有序序列的第ii个记录。直接插入排序的思想是将一个记录插入到已经排好序的有序序列中,从而得到一个新的、记录数增加1的有序表。冒泡排序的算法思想是不断在交换,通过交换完成最终的排序,每

2015-12-03 14:54:37 2286 1

原创 剑指offer系列之二:字符串空格替换

题目描述: 请实现一个函数,将一个字符串中的空格替换成”%20”。例如,当字符串为We Are Happy,则经过替换之后的字符串为We%20Are%20Happy。看到这题,我的第一思路是这样的:一组单词不是有空格嘛,所以直接使用String类的split函数直接分割为char数组不就好了,不过在这之前需要判断一下第一个位置和最后一个位置是否有空格,然后针对空格的出现情况再进行替换。发现OJ的时

2015-12-03 11:07:07 713 2

原创 剑指offer系列之一:从尾到头打印链表

题目描述: 给定一个链表,从尾部到头部打印输出链表结点的值。看到这个题目我的基本思路是:首先遍历一遍链表,统计出结点的个数,然后进行第二次遍历把每次访问的结点的值方到一个临时数组中,遍历结束之后,该临时数组中的值与正向遍历链表的值的顺序是一样的。那么在第三次遍历的时候,把上面的临时数组拷贝到另外一个临时数组中,只不过这次拷贝是从最后一个位置的值开始拷贝的,这样第三次遍历结束之后,第二个临时数组中的

2015-12-02 22:58:30 1369 2

原创 常用内部排序算法之三:堆排序

前言堆排序是以堆为原型的排序。堆首先是一棵二叉树,具有以下两个性质:每个节点的值大于或者等于其左右孩子结点的值,称为大顶堆;或者每个节点的值都小于或者等于其左右孩子结点的值,称为小顶堆。从这个定义中可以发现,堆得根节点要么是最大值要么是最小值。实现堆排序的基本思想是:将待排序的序列构造成一个大顶堆或者小顶堆。此时整个堆满足根节点是最大值或者最小值。将根节点移走,并与堆数组的最后一个值进行交换,这样的

2015-12-02 19:00:09 941

原创 Java并发编程系列之五:happens-before原则

前言happens-before是JMM的核心,之所以设计happens-before,主要出于以下两个方面的因素考虑的:1)程序员的角度,JMM内存模型需要易于理解、易于编程;2)编译器和处理器的角度,编译器和处理器希望内存模型对其束缚越少越好,这样就可以根据自己的处理规则进行优化。但是这两个方面其实是相互矛盾的,因为JMM易于编程和理解就意味着对编译器和处理器的束缚就越多。happens-bef

2015-12-02 14:34:46 6703

原创 Java并发编程系列之四:锁与volatile的内存语义

前言在前面的文章中已经提到过volatile关键字的底层实现原理:处理器的LOCK指令会使得其他处理器将缓存刷新到内存中(确切说是主存)以及会把其他处理器的缓存设置为无效。这里的内存语义则说的是在JMM中的实现,那么为什么要理解volatile和锁在JMM中的内存语义呢?主要原因是这部分内容是与程序开发息息相关的,所以在高并发量的系统中,如果对这块知识的了解欠缺的话将无法设计出优雅支持高并发的系统(

2015-12-02 13:47:12 2523 1

原创 常用内部排序算法之二:快速排序

前言快速排序可以说是内部排序算法中的高手,之所以称为快速排序,是因为快速排序算法从整体性能上讲是排序冠军。快速排序算法的思想是:通过一趟快速排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的记录的关键字小,则可分别对这两部分记录继续进行排序,达到整个记录有序。实现快速排序算法的核心是partition函数,这个函数的主要目的先选取当中的一个关键字(称为枢轴),然后尽可能将他放

2015-12-01 20:51:11 1453

原创 Java并发编程系列之三:重排序与顺序一致性

前言在我们编写程序并运行的时候,编译器给我们一个错觉:程序编译的顺序与编写的顺序是一致的。但是实际上,为了提高性能,编译器和处理器常常会对指令进行重排序。重排序主要分为两类:编译器优化的重排序、指令级别并行的重排序和内存系统的重排序。所以我们编写好Java源代码之后,会经过以上三个重排序,到最终的指令序列。我们这里提到的Java内存模型又是什么呢?Java内存模型(后面简称JMM)是语言级别的内存模

2015-12-01 15:41:10 6341

原创 常用内部排序算法之一:归并排序

前言 归并排序是所有常用内部排序算法中稳定性最好的,无论是平均时间复杂度、最坏时间复杂度还是最好时间复杂度,其时间复杂度都是O(nlogn)。由于这个特性,在需要考虑排序稳定性的情况下,归并排序是所有优化算法(直接插入排序、冒泡排序和简单选择排序)使用最多的。其实归并排序算法的思想很简单:假设初始序列含有n个记录,则可以看成是n个有序的子序列,每一个子序列的长度都是1,然后把这些子序列两两归并,得

2015-11-30 22:06:26 1228

原创 Java并发编程系列之一:并发机制的底层原理

前言并发编程的目的是让程序运行更快,但是使用并发并不定会使得程序运行更快,只有当程序的并发数量达到一定的量级的时候才能体现并发编程的优势。所以谈并发编程在高并发量的时候才有意义。虽然目前还没有开发过高并发量的程序,但是学习并发是为了更好理解一些分布式架构。那么当程序的并发量不高,比如是单线程的程序,单线程的执行效率反而比多线程更高。这又是为什么呢?熟悉操作系统的应该知道,CPU是通过给每个线程分配时

2015-11-29 19:58:56 4397 1

原创 深入理解JVM之七:静态分派与动态分派

前言这里所谓的分派指的是在Java中对方法的调用。Java中有三大特性:封装、继承和多态。分派是多态性的体现,Java虚拟机底层提供了我们开发中“重写”和“重载”的底层实现。其中重载属于静态分派,而重写则是动态分派的过程。除了使用分派的方式对方法进行调用之外,还可以使用解析调用,解析调用是在编译期间就已经确定了,在类装载的解析阶段就会把符号引用转化为直接引用,不会延迟到运行期间再去完成。而分派调用则

2015-11-28 19:53:51 5490

原创 J2EE开发技术点6:Proxool数据库连接池

前言 Proxool也是目前主流的数据库连接池,Proxool是一种Java数据库连接池技术。也是sourceforge下的一个开源项目,这个项目提供一个健壮、易用的连接池,最为关键的是这个连接池提供监控的功能,方便易用,便于发现连接泄漏的情况。下面是对Proxool连接池的一些技术小结使用Proxool连接池方式 在官方提供的文档中,有多种使用Proxool的方式,这里只介绍两种:一种是通过配

2015-11-27 17:00:50 797

原创 J2EE开发技术点5:Tomcat jdbc pool

前言 数据库连接是一种昂贵的资源,当有多个用户访问网页的时候,对程序的性能与稳定性都有要求。数据库连接池就是为了解决这个问题的,数据库连接池负责创建、管理并释放连接。连接池中的连接可以被多个程序使用,这样就降低了创建数据库连接的开销。数据库连接池的原理也很简单,就是首先在内存存放一定数量的数据库连接,当请求的时候就从连接池中获取一个连接,该请求用完数据库连接后就会释放,于是这个连接就可以被其他程序

2015-11-27 15:50:16 661

原创 J2EE开发技术点4:ajax技术

前言AJAX 是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术。需要知道的是,Ajax技术并不是一项新的技术,而是使用现有技术解决问题的新方法。Ajax(Asynchronous JavaScript and XML)也叫异步JavaScript和XML,该技术最早应用于Google maps上,也是Google把这项技术带到千家万户,可以发现目前主流的Web开发框架都集成了Aj

2015-11-26 21:07:58 1624

原创 J2EE开发技术点3:文件上传下载

前言文件上传下载在类似注册表单中经常会使用到,这里也单独抽取出来,以便日后直接使用。文件上传是以流的方式提交到服务器的,这点与普通的表单提交不同,所以需要对有文件上传域的表单特别声明,这样提交到后台的时候就可以以流的方式进行存取了。实现这个功能我们需要借助Apache的两个jar包:commons-fileupload-1.2.2.jar commons-io-2.0.1.jar。这两个jar可以

2015-11-26 17:04:29 2330

原创 J2EE开发技术点2:图形验证码

前言验证码技术被广泛运用于网站中,使用验证码可以有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上是一种登录保护措施。由于验证码是动态生成的,在使用暴力破解的时候难度加大,所以验证码技术是一种常用却简单的技术。下面就介绍如何使用Java动态生成验证码创建一个表单实现思路由于要产生随机验证码,所以有一个出现字符的范围,这里仅考虑了a-z、A-Z、0-9这个范围,然后设

2015-11-26 14:46:09 1116

原创 J2EE开发技术点1:Tomcat中开发项目

前言在以前开发Web项目的时候,使用最多往往是IDE(Eclipse EE或者MyEclipse),在IDE中我们只需要配置一下Tomcat服务器的路径就可以了,写完一个Web项目之后直接部署到Tomcat上就可以了,这一切操作都是很简单的。对于一个Web开发人员而言,使用IDE进行项目开发已经轻车熟路了。而对于在Tomcat中直接部署项目并且进行开发知道的就比较有限了。本着知其然也要知其所以然的学

2015-11-26 13:41:28 935

原创 深入理解JVM之六:类加载机制

前言虚拟机的类加载机制可以简单描述如下:Java虚拟机把描述类的数据从Class文件中加载到内存中,并对数据进行校验、解析和初始化,最终形成可以被虚拟机直接使用的Java类型。虚拟机加载进行类加载的过程是在程序运行期间完成的,在程序运行期间加载的好处是可以动态扩展,说白了就是在编译期间虚拟机是不知道要加载哪些类或者接口的,只有在程序运行的时候才知道需要加载的类。举个例子,我们在一个包下面写了很多类,

2015-11-17 14:53:40 2400

原创 深入理解JVM之五:类文件结构

前言我们平时在DOS界面中往往需要运行先运行javac命令,这个命令的直接结果就是产生相应的class文件,然后基于这个class文件才可以真正运行程序得到结果。自然。这是Java虚拟机的功劳,那么是不是Java虚拟机只能编译.java的源文件呢?答案是否定的。时至今日,Java虚拟机已经实现了语言无关性的特点。而实现语言无关性的基础是虚拟机和字节码的存储格式,Java虚拟机已经不和包括Java语言

2015-11-16 13:53:21 1337

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除