- 单例模式
public class No2 {
/**
* 设计一个类,我们只能生成该类的一个实例。
*/
public static void main(String[] args) {
}
}
//饿汉式 线程安全
class A {
private static final A a = new A();
private A() {
}
public static A getInstance() {
return a;
}
}
//懒汉式 线程安全写法
class B {
private static B b = null;
private B() {
}
public static B getInstance() {
if (b == null) {
synchronized (B.class) {
if (b == null)
b = new B();
}
}
return b;
}
}
//真正的线程安全是加上volatile,防止初始化对象和instance指向内存两个步骤重排序
class C {
private volatile static C instance;
public static C getInstance() {
if (instance == null) {
synchronized (C.class) {
if (instance == null) instance = new C(); // instance为volatile,现在没问题了
}
}
return instance;
}
}
//静态内部类的方法
class InstanceFactory {
private static class InstanceHolder {
public static C instance = new C();
}
public static C getInstance() {
return InstanceHolder.instance;//这里将导致InstanceHolder类被初始化
}
}
- 数组中重复的数字
/**
* Created by ryder on 2017/6/11. * 一个长度为n的数组,值的范围在0~n-1内,有一个或多个数字重复,求其中任意一个
*/
public class P39_DuplicationInArray {
//方法一:暴力求解,不会修改原始数据,时间复杂度o(n^2),空间复杂度o(1)
public static int getDuplication(int[] data) {
if (data == null || data.length < 2) return -1;
for (int i = 0; i < data.length - 1; i++) {
for (int j = i + 1; j < data.length; j++) {
if (data[i] == data[j]) return data[i];
}
}
return -1;
}
//方法二:排序,会修改原始数据,时间复杂度o(nlogn),空间复杂度o(1)
public static int getDuplication2(int[] data) {
if (data == null || data.length < 2)
return -1; //Arrays.sort(data); //或者使用内置函数进行排序
quickSort(data, 0, data.length - 1);
if (data.length < 2) return -1;
int prev = data[0];
for (int i = 1; i < data.length; i++) {
if (data[i] == prev) return prev;
else prev = data[i];
}
return -1;
}
public static void quickSort(int[] data, int start, int end) {
if (start >= end) return;
int bound = partion(data, start, end);
quickSort(data, start, bound - 1);
quickSort(data, bound + 1, end);
}
public static int partion(int[] data, int start, int end) {
if (start >= end) return end;
int pivot = data[start];
int left = start, right = end;
while (left < right) {
while (left < right && data[right] >= pivot) right--;
if (left < right) data[left++] = data[right];
while (left < right && data[left] < pivot) left++;
if (left < right) data[right--] = data[left];
}
data[left] = pivot;
return left;
}
//方法三:借助哈希表,不会修改原始数据,时间复杂度o(n),空间复杂度o(n)
public static int getDuplication3(int[] data) {
if (data == null || data.length < 2) return -1;
int[] hashTable = new int[data.length];
for (int item : data) {
if (hashTable[item] >= 1) return item;
else {
hashTable[item] = 1;
}
}
return -1;
}
//方法四:根据数字特点排序,会修改原始数据,时间复杂度o(n),空间复杂度o(1)
//数据特点就是范围在0~n-1
//data[i] != i表示数据不在应该的位置
//while循环直到把当前位置替换成应该的数字
public static int getDuplication4(int[] data) {
if (data == null || data.length < 2) return -1;
for (int i = 0; i < data.length; i++) {
while (data[i] != i) {
if (data[i] == data[data[i]]) return data[i];
else {
int temp = data[i];
data[i] = data[temp];
data[temp] = temp;
}
}
}
return -1;
}
//方法五:类似于二路归并,这个思路应该说是二路计数,不修改原始数据,时间复杂度o(nlogn),空间复杂度o(1)
//数值在0~n-1,也是在[start,end]间
//[start,middle]间的数字要是超过了middle-start+1,说明[start,middle]间肯定有重复数字
public static int getDuplication5(int[] data) {
if (data == null || data.length < 2)
return -1;
//数组值在[start,end]间
int start = 0;
int end = data.length - 2;
while (start <= end) {
int middle = (end - start) / 2 + start;
int count = countRange(data, start, middle);
if (start == end) {
if (count > 1) return start;
else return -1;
}
if (count > middle - start + 1) end = middle;
else start = middle + 1;
}
return -1;
}
//求在这范围的数字有几个
public static int countRange(int[] data, int start, int end) {
int count = 0;
for (int i = 0; i < data.length; i++) {
if (start <= data[i] && end >= data[i]) count++;
}
return count;
}
public static void main(String[] args) {
int[] data = {2, 3, 1, 0, 2, 5, 3};
System.out.println(getDuplication(data));
System.out.println(getDuplication2(data));
System.out.println(getDuplication3(data));
System.out.println(getDuplication4(data));
System.out.println(getDuplication5(data));
int[] data1 = {2, 3, 1, 0, 4, 5, 5};
System.out.println(getDuplication(data1));
System.out.println(getDuplication2(data1));
System.out.println(getDuplication3(data1));
System.out.println(getDuplication4(data1));
System.out.println(getDuplication5(data1));
}
}
- 二维数组中的查找
/**
* Created by ryder on 2017/6/12. * 二维数组,从左到右递增,从上到下递增,输入一个整数,判断数组中是否含有
*/
public class P44_FindInPartiallySortedMatrix {
public static boolean findInPartiallySortedMatrix(int[][] data, int target) {
if (data == null || data.length == 0 || data[0].length == 0) return false;
int rowMax = data.length - 1, colMax = data[0].length - 1;
int rowCur = data.length - 1, colCur = 0;
while (true) {
if (rowCur < 0 | rowCur > rowMax | colCur < 0 | colCur > colMax) return false;
if (data[rowCur][colCur] == target) return true;
else if (data[rowCur][colCur] > target) rowCur--;
else colCur++;
}
}
public static void main(String[] args) {
int[][] data = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
System.out.println(findInPartiallySortedMatrix(data, 10));
System.out.println(findInPartiallySortedMatrix(data, 5));
}
}
- 替换空格
public class P51_ReplaceSpaces {
//由于java的字符数组没有结束符,所以需要多传入个原始长度
// 先计算好替换后的位置,从后向前替换,时间复杂度o(n)
public static void replaceBlank(char[] data, int length) {
int newLength = length;
for (int i = 0; i < length; i++) {
if (data[i] == ' ') newLength += 2;
}
for (int indexOfOld = length - 1, indexOfNew = newLength - 1; indexOfOld >= 0 && indexOfOld != indexOfNew; indexOfOld--, indexOfNew--) {
if (data[indexOfOld] == ' ') {
data[indexOfNew--] = '0';
data[indexOfNew--] = '2';
data[indexOfNew] = '%';
} else {
data[indexOfNew] = data[indexOfOld];
}
}
}
public static void main(String[] args) {
char[] predata = "We are happy.".toCharArray();
char[] data = new char[20];
for (int i = 0; i < predata.length; i++) data[i] = predata[i];
System.out.println(data);
replaceBlank(data, 13);
System.out.println(data);
}
}
- 从尾到头打印链表
public class ListNode {
public T val;
public ListNode next;
public ListNode(T val) {
this.val = val;
this.next = null;
}
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
ret.append("[");
for (ListNode cur = this; ; cur = cur.next) {
if (cur == null) {
ret.deleteCharAt(ret.lastIndexOf(" "));
ret.deleteCharAt(ret.lastIndexOf(","));
break;
}
ret.append(cur.val);
ret.append(", ");
}
ret.append("]");
return ret.toString();
}
}
/**
* Created by ryder on 2017/6/13. * 从尾到头打印链表
*/
public class P58_PrintListInReversedOrder {
//递归版
public static void printReversinglyRecursively(ListNode node) {
if (node == null) return;
else {
printReversinglyRecursively(node.next);
System.out.println(node.val);
}
}
//非递归版
public static void printReversinglyIteratively(ListNode node) {
Stack stack = new Stack<>();
for (ListNode temp = node; temp != null; temp = temp.next)
stack.add(temp.val);
while (!stack.isEmpty())
System.out.println(stack.pop());
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
printReversinglyRecursively(head);
System.out.println();
printReversinglyIteratively(head);
}
}
二叉树的遍历
/**
* 二叉树的遍历:先序(递归,非递归),中序(递归,非递归),后序(递归,非递归),层序
*/
public class P60_TraversalOfBinaryTree {
//先序遍历递归自己写
public static void preorderRecursively(TreeNode node) {
System.out.println(node.val);
if (node.left != null)
preorderRecursively(node.left);
if (node.right != null)
preorderRecursively(node.right);
}
//先序遍历递归版
public static List preorderRecursively(TreeNode node) {
List list = new ArrayList<>();
if (node == null) return list;
list.add(node.val);
list.addAll(preorderRecursively(node.left));
list.addAll(preorderRecursively(node.right));
return list;
}
//中序遍历递归版
public static List inorderRecursively(TreeNode node) {
List list = new ArrayList<>();
if (node == null) return list;
list.addAll(inorderRecursively(node.left));
list.add(node.val);
list.addAll(inorderRecursively(node.right));
return list;
}
//后序遍历递归版
public static List postorderRecursively(TreeNode node) {
List list = new ArrayList<>();
if (node == null) return list;
list.addAll(postorderRecursively(node.left));
list.addAll(postorderRecursively(node.right));
list.add(node.val);
return list;
}
//非递归每次当前为空了,就把栈顶出栈,当前结点变为出栈结点右孩子
//先序是当前节点入栈前,把结果放到list里面
//中序是出栈之前,把结果放到list里面
//先序遍历非递归版
public static List preorderIteratively(TreeNode node) {
//stack栈顶元素永远为cur的父节点
Stack> stack = new Stack<>();
TreeNode cur = node;
List list = new LinkedList<>();
if (node == null) return list;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
list.add(cur.val);
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop().right;
}
}
return list;
}
//中序遍历非递归版
public static List inorderIteratively(TreeNode node) {
//stack栈顶元素永远为cur的父节点
Stack> stack = new Stack<>();
TreeNode cur = node;
List list = new LinkedList<>();
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
list.add(stack.peek().val);
cur = stack.pop().right;
}
}
return list;
}
//后序遍历非递归版
public static List postorderIteratively(TreeNode node) {
//stack栈顶元素永远为cur的父节点
// prevVisted用于区分是从左子树还是右子树返回的
Stack> stack = new Stack<>();
TreeNode cur = node;
TreeNode prevVisted = null;
List list = new LinkedList<>();
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else { //当前为空了,把当前节点变成栈顶的右子结点
cur = stack.peek().right;
if (cur != null && cur != prevVisted) { //不为空,且不是刚出栈
//需要判断当前节点是否刚被弹出栈,否则会多次进栈
stack.push(cur);
cur = cur.left;
} else { //当前节点还是为空或者刚出过栈,栈顶出栈并放到结果数组
prevVisted = stack.pop();
list.add(prevVisted.val);
cur = null;
}
}
}
return list;
}
//层序遍历
public static List levelorder(TreeNode node) {
Queue> queue = new LinkedList<>();
List list = new LinkedList<>();
TreeNode temp = null;
if (node == null) return list;
queue.add(node);
while (!queue.isEmpty()) {
temp = queue.poll();
list.add(temp.val);
if (temp.left != null) queue.offer(temp.left);
if (temp.right != null) queue.offer(temp.right);
}
return list;
}
public static void main(String[] args) {
// 1
// \
// 2
// /
// 3
//pre->123 in->132 post->321 level->123
TreeNode root = new TreeNode(1);
root.right = new TreeNode(2);
root.right.left = new TreeNode(3);
List list_preorderRecursively = preorderRecursively(root);
System.out.print("preorderRecursively: " + '\t');
System.out.println(list_preorderRecursively.toString());
List list_inorderRecursively = inorderRecursively(root);
System.out.print("inorderRecursively: " + '\t');
System.out.println(list_inorderRecursively.toString());
List list_postorderRecursively = postorderRecursively(root);
System.out.print("postorderRecursively: " + '\t');
System.out.println(list_postorderRecursively.toString());
System.out.println();
List list_preorderIteratively = preorderIteratively(root);
System.out.print("preorderIteratively: " + '\t');
System.out.println(list_preorderIteratively.toString());
List list_inorderIteratively = inorderIteratively(root);
System.out.print("inorderIteratively: " + '\t');
System.out.println(list_inorderIteratively.toString());
List list_postorderIteratively = postorderIteratively(root);
System.out.print("postorderIteratively: " + '\t');
System.out.println(list_postorderIteratively.toString());
System.out.println();
List list_levelorder = levelorder(root);
System.out.print("levelorder: " + '\t');
System.out.println(list_levelorder.toString());
}
}
- 重建二叉树
/**
* 重建二叉树: 先序+中序,后续+中序可以完成重建,而先序+后序无法完成
*/
public class P62_ConstructBinaryTree {
public static TreeNode construct(int[] preorder, int[] inorder) {
if (preorder == null || inorder == null || preorder.length == 0 || preorder.length != inorder.length)
return null;
//递归时需要传递的,先序和中序的开始位置初始为0,length是中序的长度
return constructCore(preorder, 0, inorder, 0, inorder.length);
}
public static TreeNode constructCore(int[] preorder, int preorder_start, int[] inorder, int inorder_start, int length) {
//length是先序的长度,为零是递归出口
if (length == 0) return null;
int inorder_index = -1;
//遍历中序的序列
for (int i = inorder_start; i < inorder_start + length; i++) {
//找到当前的根节点位置,记录到inorder_index
if (inorder[i] == preorder[preorder_start]) {
inorder_index = i;
break;
}
}
//左边中序序列的长度,右边序列长length-left_length-1
int left_length = inorder_index - inorder_start;
//根节点变成TreeNode,node的左右孩子是向下递归的结果
TreeNode node = new TreeNode(preorder[preorder_start]);
node.left = constructCore(preorder, preorder_start + 1, inorder, inorder_start, left_length);
node.right = constructCore(preorder, preorder_start + left_length + 1, inorder, inorder_index + 1, length - left_length - 1);
return node;
}
public static void main(String[] args) {
// 1
// / \
// 2 3
// / \
// 4 5
// pre->12453 in->42513 post->45231
int[] pre = {1, 2, 4, 5, 3};
int[] in = {4, 2, 5, 1, 3};
TreeNode root = construct(pre, in);
//对重建后的树,进行前中后序遍历,验证是否重建正确
// 调用的重建函数见:http://www.jianshu.com/p/362d4ff42ab2
List preorder = P60_TraversalOfBinaryTree.preorderIteratively(root);
List inorder = P60_TraversalOfBinaryTree.inorderIteratively(root);
List postorder = P60_TraversalOfBinaryTree.postorderIteratively(root);
System.out.println(preorder);
System.out.println(inorder);
System.out.println(postorder);
}
}
- 二叉树的下一个节点
/*
题目要求:
给定二叉树和其中一个节点,找到中序遍历序列的下一个节点。
树中的节点除了有左右孩子指针,还有一个指向父节点的指针。
*/
/*
思路:
(1)如果输入的当前节点有右孩子,则它的下一个节点即为该右孩子为根节点的子树的最左边的节点,比如2->5,1->3
(2)如果输入的当前节点没有右孩子,就需要判断其与自身父节点的关系:
(2.1)如果当前节点没有父节点,那所求的下一个节点不存在,返回null.
(2.2)如果输入节点是他父节点的左孩子,那他的父节点就是所求的下一个节点,比如4->2
(2.3)如果输入节点是他父节点的右孩子,那就需要将输入节点的父节点作为新的当前节点,
返回到(2),判断新的当前节点与他自身父节点的关系,比如5->1
*/
//带有父指针的二叉树节点
class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode father;
public TreeNode(int val){
this.val = val;
this.left = null;
this.right = null;
this.father = null;
}
}
public class P65_NextNodeInBinaryTrees {
public static TreeNode getNext(TreeNode pNode){
if(pNode==null)
return null;
else if(pNode.right!=null){
pNode = pNode.right;
while(pNode.left!=null)
pNode = pNode.left;
return pNode;
}
while(pNode.father!=null){
if(pNode.father.left==pNode)
return pNode.father;
pNode = pNode.father;
}
return null;
}
public static void main(String[] args){
// 1
// // \\
// 2 3
// // \\
// 4 5
// inorder->42513
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.left.father = root;
root.right = new TreeNode(3);
root.right.father = root;
root.left.left = new TreeNode(4);
root.left.left.father = root.left;
root.left.right = new TreeNode(5);
root.left.right.father = root.left;
System.out.println(getNext(root.left.left).val);
System.out.println(getNext(root.left).val);
System.out.println(getNext(root.left.right).val);
System.out.println(getNext(root).val);
System.out.println(getNext(root.right));
}
}
- 用两个栈实现队列
/*
思路:
(1)对于插入操作,栈与队列都是从队尾进行,因此一行代码就可以完成offer()
(2)对于弹出操作,队列先进先出从队头开始,而栈后进先出从队尾开始,要想取到队头元素,
就得需要第二个栈stack2的协助:弹出时将stack1的元素依次取出放到stack2中,此时stack2
进行弹出的顺序就是整个队列的弹出顺序。而如果需要插入,放到stack1中即可。
*/
//stack2有值就2出栈,否则将1导入2,再出栈
class MyQueue{
private Stack stack1 = new Stack<>();
private Stack stack2 = new Stack<>();
public void offer(T data){
stack1.push(data);
}
public T poll(){
if(!stack2.isEmpty()){
return stack2.pop();
}
else if(!stack1.isEmpty()){
while(!stack1.isEmpty())
stack2.push(stack1.pop());
return stack2.pop();
}
else
return null;
}
}
public class P68_QueueWithTwoStacks {
public static void main(String[] args){
MyQueue myQueue = new MyQueue<>();
System.out.println(myQueue.poll());
myQueue.offer(1);
myQueue.offer(2);
myQueue.offer(3);
System.out.println(myQueue.poll());
System.out.println(myQueue.poll());
myQueue.offer(4);
System.out.println(myQueue.poll());
System.out.println(myQueue.poll());
System.out.println(myQueue.poll());
}
}
- 斐波那契数列
/**
* Created by ryder on 2017/6/21.
* 斐波那契数列
* f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2) n>1
*/
解法 解法介绍 时间复杂度 空间复杂度
解法1 依定义递归求解 o(n^2) o(1)
解法2 从0开始迭代求解 o(n) o(1)
解法3 借助等比数列公式 o(logn) o(1)
解法4 借助通项公式 o(1) o(1)
public class P74_Fibonacci {
// 依据原始概念的递归解法,时间复杂度o(n^2)
public static int fibonacci1(int n){
if(n<=0)
return 0;
if(n==1)
return 1;
return fibonacci1(n-1)+fibonacci1(n-2);
}
// 当前状态只与前两个状态有关。存储前两个值,计算后一个,迭代进行。时间复杂度o(n)
public static int fibonacci2(int n){
if(n<=0)
return 0;
if(n==1)
return 1;
int temp1 =0,temp2=1;
int result = temp1 + temp2,i=3;
while(i<=n){
//也可用一个队列来完成下面三行的操作
temp1 = temp2;
temp2 = result;
result = temp1+temp2;
i++;
}
return result;
}
// 借助如下数学公式解决问题。矩阵乘法部分,可用递归解决,时间复杂度o(logn)
// [ f(n) f(n-1) ] = [ 1 1 ] ^ n-1 (当n>2)
// [f(n-1) f(n-2) ] [ 1 0 ]
// 证明:
// [ f(n) f(n-1) ] = [ f(n-1)+f(n-2) f(n-1)] = [ f(n-1) f(n-2)] * [1 1]
// [f(n-1) f(n-2) ] [ f(n-2)+f(n-3) f(n-2)] [ f(n-2) f(n-3)] [1 0]
// 得到如上递推式,所以
// [ f(n) f(n-1) ] = [ f(2) f(1)] * [1 1]^n-2 = [1 1]^n-1
// [f(n-1) f(n-2) ] [ f(1) f(0)] [1 0] [1 0]
public static int fibonacci3(int n){
int[][] start = {{1,1},{1,0}};
return matrixPow(start,n-1)[0][0];
}
public static int[][] matrixPow(int[][] start,int n){
if((n&1)==0){
int[][] temp = matrixPow(start,n>>1);
return matrixMultiply(temp,temp);
}
else if(n==1){
return start;
}
else{
return matrixMultiply(start,matrixPow(start,n-1));
}
}
public static int[][] matrixMultiply(int[][] x,int[][] y){
int[][] result = new int[x.length][y[0].length];
for(int i=0;i
排序算法比较表格
https://www.jianshu.com/p/6ae77d17170c
快排:
package chapter2;
public class P79_Sort {
//数组快排,时间o(nlogn)(最差n^2),空间o(logn)(最差n),递归造成的栈空间的使用,不稳定
public static void quickSort(int[] data){
if(data==null || data.length<=1) return;
quickSortCore(data,0,data.length-1);
}
public static void quickSortCore(int[] data,int start,int end){
if(end-start<=0)
return;
int index = quickSortPartition(data,start,end);
quickSortCore(data,start,index-1);
quickSortCore(data,index+1,end);
}
public static int quickSortPartition(int[] data,int start,int end){
//选择第一个值作为基准
int pivot = data[start];
int left = start,right = end;
while(left=pivot)
right--;
if(left=end)
return;
int mid = start + (end - start)/2;
mergeSortCore(data,start,mid);
mergeSortCore(data,mid+1,end);
mergeSortMerge(data,start,mid,end);
}
public static void mergeSortMerge(int[] data,int start,int mid,int end){
if(end==start)
return;
int[] temp = new int[end-start+1];
int left = start,right = mid+1,tempIndex = 0;
while(left<=mid && right<=end){
if(data[left] num[j]) {
break;
}
//如果小于则将子节点移动到父节点位置
num[i] = num[j];
//继续向下调整
i = j;
}
//最后插入数据
num[i] = temp;
}
public static void HeapInit(int[] nums, int l) {
for (int i = (l - 1) / 2; i >= 0; i--) {
HeapAdjust(nums, i, l);
System.out.println(Arrays.toString(nums)+", i:"+i);
}
}
public static void HeapSort(int[] nums, int l) {
for (int i = l; i > 0; i--) {
//把大根堆的跟放到最后的位置也就是i
int temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
//然后每次都从根也就是0开始调堆,不调最后排好的位置
HeapAdjust(nums, 0, i - 1);
System.out.println(Arrays.toString(nums)+", l:"+i);
}
}
public static void main(String[] args) {
int[] nums = {0, 1, 2, 3, 4, 5, 6, 7, 8};
HeapInit(nums, 8);
HeapSort(nums, 8);
System.out.println(Arrays.toString(nums));
}
}
冒泡排序:
package chapter2;
/**
* Created by ryder on 2017/6/25.
* 数组排序算法
*/
public class P79_Sort {
//数组冒泡,时间o(n^2),空间o(1),稳定
public static void bubbleSort(int[] data){
if(data==null || data.length<=1)
return;
for(int i=0;idata[j]){
int temp = data[j-1];
data[j-1] = data[j];
data[j] = temp;
}
}
}
}
public static void testBubbleSort(){
int[] data = {5,4,3,1,2};
bubbleSort(data);
System.out.print("数组冒泡排序:\t");
for(int item: data){
System.out.print(item);
System.out.print('\t');
}
System.out.println();
}
}
选择排序:
package chapter2;
/**
* Created by ryder on 2017/6/25.
* 数组排序算法
*/
public class P79_Sort {
//数组选择排序,时间o(n^2),空间o(1),不稳定
public static void selectionSort(int[] data){
if(data==null || data.length<=1)
return;
for(int i=0;i0 && data[j-1]>temp) {
data[j] = data[j-1];
j--;
}
data[j] = temp;
}
}
public static void testInsertionSort(){
int[] data = {5,4,3,1,2};
insertionSort(data);
System.out.print("数组插入排序:\t");
for(int item: data){
System.out.print(item);
System.out.print('\t');
}
System.out.println();
}
}
希尔排序:
package chapter2;
/**
* Created by ryder on 2017/6/25.
* 数组排序算法
*/
public class P79_Sort {
//数组希尔排序(插入排序缩小增量),时间o(n^1.3),空间o(1),不稳定
//时间复杂度是模糊的,有人在大量的实验后得出结论:
//当n在某个特定的范围后希尔排序的比较和移动次数减少至n^1.3。次数取值在1到2之间。
public static void shellSort(int[] data){
if(data==null || data.length<=1)
return;
for(int d=data.length/2; d>0; d=d/2){
for(int i=d;i=d && data[cur-d]>temp){
data[cur] = data[cur-d];
cur = cur - d;
}
data[cur] = temp;
}
}
}
public static void testShellSort(){
int[] data = {5,4,3,1,2};
shellSort(data);
System.out.print("数组希尔排序:\t");
for(int item: data){
System.out.print(item);
System.out.print('\t');
}
System.out.println();
}
}
- 旋转数组的最小数字
/**
* Created by ryder on 2017/6/28.
* 旋转数组的最小数字
*/
//把一个数组的若干元素搬到数组末尾叫旋转数组
public class P82_MinNumberInRotatedArray {
public static int min(int[] data){
if(data==null || data.length==0)
return -1;
int left = 0;
int right = data.length-1;
int mid;
while(left right
else if(data[left]>data[right]){
//中间的大等左边,说明最小值在mid右边
if(data[mid]>=data[left])
left = mid + 1;
//否则在mid左边,包含mid
else
right = mid;
}
//left = right
else{
//中间大于左边,说明最小值在mid右边
if(data[left]data[mid])
right = mid;
//不确定,只能缩小两个位置的范围
else{
left = left+1;
right = right-1;
}
}
}
return data[right];
}
public static void main(String[] args){
int[] data1 = {3,4,5,1,2};
int[] data2 = {1,0,1,1,1};
int[] data3 = {1,1,1,0,1};
System.out.println(min(data1));
System.out.println(min(data2));
System.out.println(min(data3));
}
}
- 矩阵中的路径
题目要求:设计一个函数,用来判断一个矩阵中是否存在一条包含某字符串的路径。
(1)起点随意;(2)路径的移动只能是上下左右;(3)访问过的位置不能再访问。
以下图矩阵为例,包含“bfce”,但是不包含“abfb”。
a b t g
c f c s
j d e h
/**
* Created by ryder on 2017/7/2.
* 矩阵中的路径
*/
public class P89_StringPathInMatrix {
//回溯法解决
public static boolean hasPath(char[][] data,String str){
if(data==null || data.length==0 || str==null || str.length()==0)
return false;
int rowLen = data.length;
int colLen = data[0].length;
boolean[][] visitFlag = new boolean[rowLen][colLen];
for(int row=0;row=str.length()) return true;
if(rowIndex<0 || colIndex<0 || rowIndex>=data.length || colIndex>=data[0].length)
return false;
//递归
if(!visitFlag[rowIndex][colIndex]&&data[rowIndex][colIndex]==str.charAt(strIndex)){
//如果未被访问,且匹配字符串,标记当前位置为已访问,分上下左右四个位置递归求解
visitFlag[rowIndex][colIndex] = true;
boolean result =
hasPathCore(data,rowIndex+1,colIndex,visitFlag,str,strIndex+1) ||
hasPathCore(data,rowIndex-1,colIndex,visitFlag,str,strIndex+1) ||
hasPathCore(data,rowIndex,colIndex+1,visitFlag,str,strIndex+1) ||
hasPathCore(data,rowIndex,colIndex-1,visitFlag,str,strIndex+1);
//已经求的结果,无需修改标记了
if(result)
return true;
//当前递归的路线求解失败,要把这条线路上的标记清除掉
//因为其他起点的路径依旧可以访问本路径上的节点。
else{
visitFlag[rowIndex][colIndex] = false;
return false;
}
}
else
return false;
}
public static void main(String[] args){
char[][] data = {
{'a','b','t','g'},
{'c','f','c','s'},
{'j','d','e','h'}};
System.out.println(hasPath(data,"bfce")); //true
System.out.println(hasPath(data,"abfb")); //false,访问过的位置不能再访问
}
}
- 机器人的运动范围
题目要求:
地上有一个m行n列的方格,一个机器人从坐标(0,0)的各自开始移动,它每次
可以向上下左右移动一格,但不能进入横纵坐标数位之和大于k的格子。
例如,当k等于18时,机器人能进入(35,37),因为3+5+3+7=18;但却不能进入
(35,38),因为3+5+3+8=19>18。
请问该机器人能够到达多少个格子。
解题思路:
本题依旧考察回溯法。
每前进一步后,可选移动项为上下左右四个;为了判断某一个格子是否可以进入
从而进行计数,不仅需要考虑边界值,计算各位数字之和,更要判断该格子是否
已经被访问过,。所以需要一个布尔矩阵,用来记录各格子是否已被访问。整体思
路与12题类似,具体请参考本系列的导航帖。
package chapter2;
/**
* Created by ryder on 2017/7/4.
* 机器人的运动范围
*/
public class P92_RobotMove {
//依旧回溯
public static int movingCount(int threshold,int rowLen,int colLen){
if(rowLen<=0 || colLen<=0 || threshold<0)
return 0;
boolean[][] visitFlag = new boolean[rowLen][colLen];
for(int row=0;row=0 && col>=0 && row0){
sum += number%10;
number/=10;
}
return sum;
}
public static void main(String[] args){
System.out.println(movingCount(0,3,4)); //1
System.out.println(movingCount(1,3,4)); //3
System.out.println(movingCount(9,2,20)); //36
}
}
- 剪绳子
题目要求:
给你一根长度为n的绳子,请把绳子剪成m段,记每段绳子长度为 k[0],k[1]...k[m-1],
求k[0]k[1]...k[m-1]的最大值。已知绳子长度n为整数,m>1(至少要剪一刀,不能不剪),
k[0],k[1]...k[m-1]均要求为整数。
例如,绳子长度为8时,把它剪成3-3-2,得到最大乘积18;绳子长度为3时,把它剪成2-1,
得到最大乘积2。
我们定义长度为n的绳子剪切后的最大乘积为f(n),剪了一刀后,f(n)=max(f(i)*f(n-i));
假设n为10,第一刀之后分为了4-6,而6也可能再分成2-4(6的最大是3-3,但过程中还是
要比较2-4这种情况的),而上一步4-6中也需要求长度为4的问题的最大值,可见,各个子
问题之间是有重叠的,所以可以先计算小问题,存储下每个小问题的结果,逐步往上,求得
大问题的最优解。
上述算法的时间复杂度为o(n^2);但其实,可以使用贪婪算法在o(1)时间内得到答案:
n<5时,和动态规划一样特殊处理;n>=5时,尽可能多地剪长度为3的绳子,当剩下的绳子长
度为4时,剪成2-2;比如长度为13的绳子, 剪成3-3-3-2-2;贪婪算法虽然快,但一般都思
路奇特,可遇不可求。且面试官一般都会要求证明,数学功底要好 。
/**
* Created by ryder on 2017/7/5.
* 剪绳子
*/
public class P96_CuttingRope {
public static int maxCutting(int length){
if(length<2) return 0;
if(length==2)return 1;
if(length==3)return 2;
int[] dp = new int[length+1];
dp[0]=0;
dp[1]=1;
dp[2]=2;
dp[3]=3;
int max = 0;
int temp = 0;
//i是绳子的长度,j是最后一次减去的长度,到总长度的一半
for(int i=4;i<=length;i++){
max = 0;
for(int j=1;j<=i/2;j++){
temp = dp[j]*dp[i-j];
if(temp>max)
max = temp;
}
//dp[i]是temp里最大的
dp[i] = max;
}
return dp[length];
}
public static void main(String[] args){
for(int i=2;i<10;i++){
System.out.println("长度为"+i+"的最大值->"+maxCutting(i));
}
}
}
- 位运算
题目要求:
实现一个函数,输入一个int型整数,输出该数字在计算机中二进制表示形式的1的个数。
例如9->1001,输出2;-3->11111111111111111111111111111101,输出31。
解题思路:
考查位运算,此题要注意负数的处理。首先要明确计算机中,数字是以补码的形式存储的,
原码反码补码不清楚的话请自己谷歌百度。其次,明确位运算符,与&,或|,非~,异或^,
<<左移位,>>带符号右移位,>>>无符号右移位(java有此符号,c++没有)
解法一:将数字无符号右移,直到为0。
解法二:使用一个标记,初始为1,让标记值与原输入数字异或,然后标记值左移。解法一
是原数字右移,而解法二是标记左移,从java来看思路类似但换了个角度;但这个思路在
C++就很关键,因为C++中没有>>>运算符,只能用解法二。
解法三:没接触过的人应该会觉得比较新颖。对于二进制数有如下结论:【把一个整数减去1
之后再和原来的整数做位与运算,得到的结果相当于把原整数的二进制表示形式的最右边的
1变成0】。比如1001,执行一次上述结论,1001&1000=1000,将最右边的1改为了0;再执行
一次,1000&0111=0000,第二个1也改成了0。因此能执行几次该结论,就有几个1。
对于解法一二,都需要循环32次,判断每一个比特位是否为1,而解法三,循环次数等于比特
位为1的个数。时间上是有改进的。
/**
* Created by ryder on 2017/7/6.
* 二进制中的1的个数
*/
public class P100_NumberOf1InBinary {
public static int numberOfOne1(int n){
int count=0;
while(n!=0){
if((n&1)!=0)
count++;
n>>>=1;
}
return count;
}
public static int numberOfOne2(int n){
int count=0;
int flag=1;
while(flag!=0){
if((n&flag)!=0)
count++;
flag<<=1;
}
return count;
}
public static int numberOfOne3(int n){
int count=0;
while(n!=0){
n = n&(n-1);
count++;
}
return count;
}
public static void main(String[] args){
System.out.println(numberOfOne1(3));
System.out.println(numberOfOne1(-3));
System.out.println(numberOfOne2(3));
System.out.println(numberOfOne2(-3));
System.out.println(numberOfOne3(3));
System.out.println(numberOfOne3(-3));
}
}
- 数值的整数次方
题目要求:
实现函数double power(double base,int exponent),求base的exponent次方。
不能使用库函数,不需要考虑大数问题。
解题思路:本题考查考虑问题的完整性。如下几个点要注意:
要考虑一些特殊情况,如指数为负、指数为负且底数为0、0的0次方要定义为多少。
底数为0的定义。对于一个double类型的数,判断它与另一个数是否相等,不能用“==”,
一般都需要一个精度,见下面的equal函数。
对于报错的情况,比如0的负数次方,要如何处理。书中提了三种错误处理方法:用函数
返回值来提示错误;用一个全局变量来提示错误;抛出异常;三种方法优缺点比较如下
错误处理方法 优点 缺点
返回值 和相关函数的API一致 不能方便地使用计算结果
全局变量 能够方便地使用计算结果 用户可能忘记检查全局变量
异常 可自定义异常类型,逻辑清晰明了 抛出异常时对性能有负面影响
/**
* Created by ryder on 2017/7/6.
* 数值的整数次方
*/
public class P110_Power {
static boolean invalidInput = false;
public static double power(double base,int exponent){
//0的0次方在数学上没有意义,为了方便也返回1,也可特殊处理
if(exponent==0)
return 1;
if(exponent<0){
if(equal(base,0)){
//通过全局变量报错
invalidInput = true;
return 0;
}
else
return 1.0/powerWithPositiveExponent(base,-1*exponent);
}
else
return powerWithPositiveExponent(base,exponent);
}
public static boolean equal(double x,double y){
return -0.00001>1);
return temp*temp;
}
else{
//为奇数,例如五次方,就是两个二次方再乘以一个base
double temp = powerWithPositiveExponent(base,exponent>>1);
return base*temp*temp;
}
}
public static void main(String[] args){
System.out.println("2^3="+power(2,3)+"\t是否报错:"+invalidInput);
System.out.println("2^-3="+power(2,-3)+"\t是否报错:"+invalidInput);
System.out.println("0^3="+power(0,3)+"\t是否报错:"+invalidInput);
System.out.println("0^-3="+power(0,-3)+"\t是否报错:"+invalidInput);
}
}
- 打印从1到最大的n位数
题目要求:
比如输入2,打印1,2......98,99;
解题思路:
此题需要考虑大数问题。本帖是使用字符串模拟数字的加法。
/**
* Created by ryder on 2017/7/6.
*
*/
public class P114_Print1ToMaxOfNDigits {
//在字符串上模拟加法
public static void print1ToMaxOfNDigits(int num){
if(num<=0)
return;
StringBuilder number = new StringBuilder(num);
for(int i=0;i=0;i--){
if(str.charAt(i)<'9' && str.charAt(i)>='0'){
str.setCharAt(i,(char)(str.charAt(i)+1));
return true;
}
else if(str.charAt(i)=='9'){
str.setCharAt(i,'0');
}
else{
return false;
}
}
return false;
}
//打印时从不为0的开始打印
public static void printNumber(StringBuilder number){
boolean flag = false;
for(int i=0;i
- 删除链表的节点
题目要求:
在o(1)时间内删除单链表的节点。
解题思路:
直接删除单链表某一节点,无法在o(1)时间得到该节点的前一个节点,
因此无法完成题目要求。可以将欲删节点的后一个节点的值拷贝到欲删
节点之上,删除欲删节点的后一个节点,从而可以在o(1)时间内完成
删除。(对于尾节点,删除仍然需要o(n),其他点为o(1),因此平均时
间复杂度为o(1),满足要求)
package structure;
/**
* Created by ryder on 2017/6/13.
*/
public class ListNode {
public T val;
public ListNode next;
public ListNode(T val){
this.val = val;
this.next = null;
}
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
ret.append("[");
for(ListNode cur = this;;cur=cur.next){
if(cur==null){
ret.deleteCharAt(ret.lastIndexOf(" "));
ret.deleteCharAt(ret.lastIndexOf(","));
break;
}
ret.append(cur.val);
ret.append(", ");
}
ret.append("]");
return ret.toString();
}
}
package chapter3;
import structure.ListNode;
/**
* Created by ryder on 2017/7/7.
* o(1)时间删除链表的节点
*/
public class P119_DeleteNodeInList {
public static ListNode deleteNode(ListNode head,ListNode node){
if(node==head){
return head.next;
}
//考虑下一个为空和不为空的情况
else if(node.next!=null){
node.val = node.next.val;
node.next = node.next.next;
return head;
}
else{
ListNode temp=head;
while(temp.next!=node)
temp = temp.next;
temp.next = null;
return head;
}
}
public static void main(String[] args){
ListNode head = new ListNode<>(1);
ListNode node2 = new ListNode<>(2);
ListNode node3 = new ListNode<>(3);
head.next = node2;
node2.next = node3;
System.out.println(head);
head = deleteNode(head,node3);
System.out.println(head);
head = deleteNode(head,head);
System.out.println(head);
}
}
- 题目二:删除排序链表中重复的节点
题目要求:
比如[1,2,2,3,3,3],删除之后为[1];
解题思路:
由于是已经排序好的链表,需要确定重复区域的长度,删除后还需要将被删去的前与后连接,
所以需要三个节点pre,cur,post,cur-post为重复区域,删除后将pre与post.next连接即可。
此外,要注意被删结点区域处在链表头部的情况,因为需要修改head。
package structure;
/**
* Created by ryder on 2017/6/13.
*/
public class ListNode {
public T val;
public ListNode next;
public ListNode(T val){
this.val = val;
this.next = null;
}
@Override
public String toString() {
StringBuilder ret = new StringBuilder();
ret.append("[");
for(ListNode cur = this;;cur=cur.next){
if(cur==null){
ret.deleteCharAt(ret.lastIndexOf(" "));
ret.deleteCharAt(ret.lastIndexOf(","));
break;
}
ret.append(cur.val);
ret.append(", ");
}
ret.append("]");
return ret.toString();
}
}
package chapter3;
import structure.ListNode;
/**
* Created by ryder on 2017/7/7.
* 删除排序链表中的重复节点
*/
public class P122_deleteDuplicatedNode {
public static ListNode deleteDuplication(ListNode head){
if(head==null||head.next==null)
return head;
ListNode pre = null;
ListNode cur = head;
ListNode post = head.next;
boolean needDelete = false;
while (post!=null){
if(cur.val.equals(post.val)){
needDelete = true;
post=post.next;
}
else if(needDelete && !cur.val.equals(post.val)){
if(pre==null)
head = post;
else
pre.next=post;
cur = post;
post = post.next;
needDelete = false;
}
else{
pre = cur;
cur = post;
post = post.next;
}
}
if(needDelete && pre!=null)
pre.next = null;
else if(needDelete && pre==null)
head = null;
return head;
}
public static void main(String[] args){
ListNode head = new ListNode<>(1);
head.next= new ListNode<>(1);
head.next.next = new ListNode<>(2);
head.next.next.next = new ListNode<>(2);
head.next.next.next.next = new ListNode<>(2);
head.next.next.next.next.next = new ListNode<>(3);
System.out.println(head);
head = deleteDuplication(head);
System.out.println(head);
}
}
- 正则表达式匹配
题目要求:
实现正则表达式中.和*的功能。.表示任意一个字符,*表示他前面的字符的任意次(含0次)。
比如aaa与a.a和ab*ac*a匹配,但与aa.a和ab*a不匹配。
解题思路:
.就相当于一个万能字符,正常匹配即可;但*的匹配会涉及到前一个字符。所以要分模式串后
一个字符不是*或没有后一个字符,模式串后一个字符是*这几个大的情况,之再考虑.的问题。
package chapter3;
/**
* Created by ryder on 2017/7/13.
* 正则表达式匹配
* 完成.(任何一个字符)和*(前面字符的任意次数)
*/
public class P124_RegularExpressionsMatching {
public static boolean match(String str,String pattern){
if(str==null || pattern==null)
return false;
return matchCore(new StringBuilder(str),0,new StringBuilder(pattern),0);
}
public static boolean matchCore(StringBuilder str,Integer strIndex,StringBuilder pattern, Integer patternIndex){
//如果匹配串和模式串都匹配结束
if(strIndex==str.length() && patternIndex==pattern.length())
return true;
if(strIndex!=str.length() && patternIndex==pattern.length())
return false;
if(strIndex==str.length() && patternIndex!=pattern.length()) {
if(patternIndex+1
- 表示数值的字符串
题目要求:
判断一个字符串是否表示数值,如+100,5e2,-123,-1E-16都是,
12e,1e3.14,+-5,1.2.3,12e+5.4都不是。
提示:表示数值的字符串遵循模式A[.[B]][e|EC] 或者 .B[e|EC];
A,B,C表示整数,|表示或。[]表示可有可无。
解题思路:
此题也没有没什么特殊思路,就按照A[.[B]][e|EC] 或者 .B[e|EC];
A,B,C这两种模式匹配下即可。
package chapter3;
/**
* Created by ryder on 2017/7/13.
* 表示数值的字符串
*/
public class P127_NumberStrings {
public static boolean isNumeric(String str){
//正确的形式:A[.[B]][e|EC] 或者 .B[e|EC];
if(str==null||str.length()==0)
return false;
int index;
if(str.charAt(0)!='.'){
index = scanInteger(str,0);
if(index==-1)
return false;
if(index==str.length())
return true;
if(str.charAt(index)=='.'){
if(index==str.length()-1)
return true;
index = scanInteger(str,index+1);
if(index==str.length())
return true;
}
if(str.charAt(index)=='e'||str.charAt(index)=='E'){
index = scanInteger(str,index+1);
if(index==str.length())
return true;
else
return false;
}
return false;
}
else{
index = scanInteger(str,1);
if(index==-1)
return false;
if(index==str.length())
return true;
if(str.charAt(index)=='e'||str.charAt(index)=='E'){
index = scanInteger(str,index+1);
if(index==str.length())
return true;
}
return false;
}
}
public static int scanInteger(String str,Integer index){
if(index>=str.length())
return -1;
if(str.charAt(index)=='+'||str.charAt(index)=='-')
return scanUnsignedInteger(str,index+1);
else
return scanUnsignedInteger(str,index);
}
public static int scanUnsignedInteger(String str,Integer index){
int origin = index;
while(str.charAt(index)>='0'&&str.charAt(index)<='9'){
index++;
if(index==str.length())
return index;
}
if(origin==index)
index = -1;
return index;
}
public static void main(String[] args){
System.out.println(isNumeric("+100"));//true
System.out.println(isNumeric("5e2")); //true
System.out.println(isNumeric("-123"));//true
System.out.println(isNumeric("3.1416"));//true
System.out.println(isNumeric("-1E-16"));//true
System.out.println(isNumeric(".6"));//true
System.out.println(isNumeric("6."));//true
System.out.println(isNumeric("12e"));//false
System.out.println(isNumeric("1a3.14"));//false
System.out.println(isNumeric("1.2.3"));//false
System.out.println(isNumeric("+-5"));//false
System.out.println(isNumeric("12e+5.4"));//false
}
}