02 单例模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
从具体实现角度来说,主要有三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
// 懒汉式-线程不安全
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
// 双重校验锁-线程安全
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
// 静态内部类实现
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
03 数组中重复的数字
/**
* 03 数组中重复的数字
* 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道 有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
*/
public class _03 {
public static boolean duplicate(int[] numbers,int length,int[] duplication) {
int [] array = new int[length];
for (int i = 0; i < length; ++i) {
if (array[numbers[i]] == 0) {
array[numbers[i]] = 1;
} else {
duplication[0] =numbers[i];
return true;
}
}
return false;
}
public static void main(String[] args) {
int[] numbers = {2, 3, 1, 0, 2, 5, 3};
int[] duplication = new int[1];
System.out.println(duplicate(numbers, numbers.length, duplication) + " " + duplication[0]);
}
}
04 二维数组中的查找
/**
* 04 二维数组中的查找
* 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
*/
public class _04 {
public static boolean find(int target, int [][] array) {
if (array == null || array.length == 0) {
return false;
}
if (array[0].length == 0) {
return false;
}
int col = array[0].length;
int row = array.length;
int i = row - 1;
int j = 0;
while (j < col && i >= 0) {
if (array[i][j] == target) {
return true;
} else if (array[i][j] > target) {
i--;
} else {
j++;
}
}
return false;
}
public static void main(String[] args) {
int[][] array = {{1, 4, 7, 11, 15},
{2, 5, 8, 12, 19}};
System.out.println("Find(5, array) = " + find(5, array));
}
}
05 替换空格
/**
* 05 替换空格
* 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过 替换之后的字符串为We%20Are%20Happy。
*
* 在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。因为一个空格要替换成三个字符 (%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。
* 令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2 从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。
* 从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容
*/
public class _05 {
public static String replaceSpace(StringBuffer str) {
int originalLength = str.length();
for (int i = 0; i < originalLength; ++i) {
if (str.charAt(i) == ' ') {
// 每遇到一个空格扩充2个长度
str.append(" ");
}
}
int newLength = str.length();
int j = newLength - 1;
for (int i = originalLength - 1; i >= 0; --i) {
if (str.charAt(i) == ' ') {
str.setCharAt(j--, '0');
str.setCharAt(j--, '2');
str.setCharAt(j--, '%');
} else {
str.setCharAt(j--, str.charAt(i));
}
}
return str.toString();
}
public static void main(String[] args) {
replaceSpace(new StringBuffer("We Are Happy"));
}
}
06 从头到尾打印链表
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 06 从头到尾打印链表
* 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
*/
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
public class _06 {
/**
* 使用堆栈
* @param listNode 链表头结点
* @return 反向打印所有值
*/
public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if (listNode == null) {
return new ArrayList<>();
}
Stack<Integer> stack = new Stack<>();
ArrayList<Integer> arrayList = new ArrayList<>();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
while (!stack.empty()) {
arrayList.add(stack.pop());
}
return arrayList;
}
/**
* 使用递归
* @param listNode 链表头结点
* @return 反向打印所有值
*/
public static ArrayList<Integer> printListFromTailToHead1(ListNode listNode) {
if (listNode == null) {
return new ArrayList<>();
}
ArrayList<Integer> arrayList = printListFromTailToHead1(listNode.next);
arrayList.add(listNode.val);
return arrayList;
}
/**
* 使用头插法
* 构建一个头指针head,初始head->next = null,然后不断将listNode中的节点加到head后 面,相当于反向链表
* @param listNode 链表头结点
* @return 反向打印所有值
*/
public static ArrayList<Integer> printListFromTailToHead2(ListNode listNode) {
if (listNode == null) {
return new ArrayList<>();
}
// 头指针
ListNode head = new ListNode(0);
head.next = null;
// 将listNode中的结点逐步加到head后面
while (listNode != null) {
// 先存储下一个结点
ListNode nextNode = listNode.next;
// 头插
listNode.next = head.next;
head.next = listNode;
// 继续下一次循环
listNode = nextNode;
}
ArrayList<Integer> arrayList = new ArrayList<>();
// 头结点
listNode = head.next;
while (listNode != null) {
arrayList.add(listNode.val);
listNode = listNode.next;
}
return arrayList;
}
public static void main(String[] args) {
// 构造一个链表
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
listNode2.next = null;
listNode1.next = listNode2;
List<Integer> list = printListFromTailToHead2(listNode1);
for (Integer integer : list) {
System.out.println(integer + " ");
}
}
/**
* 内部类:相当于一个链表节点
*/
public static class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
}
07 重建二叉树
/**
* 07 重建二叉树
* 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结 果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列 {4,7,2,1,5,3,8,6},则重建二叉树并返回。
* 前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序 遍历结果,右部分为树的右子树中序遍历的结果,然后就可以接着分别对左右子树递归下去。
*/
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class _07 {
/**
* 重建二叉树
* @param pre 前序遍历
* @param in 中序遍历
* @return 返回重建的二叉树
*/
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if (pre == null || in == null || pre.length == 0 || in.length == 0) {
return null;
}
return reConstructBinaryTree(pre, in, 0, pre.length - 1, 0, in.length - 1);
}
/**
* 扩充前序与中序的索引参数,便于递归重建二叉树
* @param pre 前序遍历
* @param in 中序遍历
* @param preStart 前序遍历数组的开始索引
* @param preEnd 前序遍历数组的结束索引
* @param inStart 中序遍历数组的开始索引
* @param inEnd 中序遍历数组的结束索引
* @return 返回重建的二叉树
*/
private static TreeNode reConstructBinaryTree(int [] pre,int [] in, int preStart, int preEnd, int inStart, int inEnd) {
if (preStart > preEnd) {
return null;
}
// 根节点的值是前序遍历的第一个值
int rootValue = pre[preStart];
TreeNode treeNode = new TreeNode(rootValue);
// 找到中序遍历序列中的根节点的位置,递归得到左右节点
for (int i = inStart; i <= inEnd; ++i) {
if (in[i] == pre[preStart]) {
treeNode.left = reConstructBinaryTree(pre, in, preStart + 1, preStart + i - inStart, inStart, i - 1);
treeNode.right = reConstructBinaryTree(pre, in, preStart + i - inStart + 1, preEnd, i + 1, inEnd);
break;
}
}
return treeNode;
}
public static void main(String[] args) {
int[] pre = {1, 2, 4, 7, 3, 5, 6, 8};
int[] in = {4, 7, 2, 1, 5, 3, 8, 6};
reConstructBinaryTree(pre, in);
}
/**
* 内部类:二叉树结构
*/
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
}
08 二叉树的下一个结点
/**
* 08 二叉树的下一个结点
* 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不 仅包含左右子结点,同时包含指向父结点的指针
* 分三种情况:
* (1)当前节点有右子树,则下一个中序遍历节点是右子树中的最左节点
* (2)当前节点没有右子树,且该节点是其父节点的左节点,则下一个节点是其父节点
* (3)当前节点没有右子树,且该节点是其父节点的右节点,则沿着其父节点向上遍历,直到找到一个 是其父节点的左节点的节点,这个节点的父节点即为所求
*/
/**
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class _08 {
public static TreeLinkNode GetNext(TreeLinkNode pNode) {
if (pNode == null) {
return null;
}
// 当前节点有右子树
if (pNode.right != null) {
pNode = pNode.right;
while (pNode.left != null) {
pNode = pNode.left;
}
return pNode;
}
// 当前节点没有右子树,且该节点没有父节点,是根节点
if (pNode.next == null) {
return null;
}
// 当前节点没有右子树,且该节点是其父节点的左节点
if (pNode.next.left == pNode) {
return pNode.next;
}
// 当前节点没有右子树,且该节点是其父节点的右节点
if (pNode.next.right == pNode) {
pNode = pNode.next;
while (pNode.next != null) {
if (pNode.next.left != pNode) {
pNode = pNode.next;
} else {
return pNode.next;
}
}
}
return null;
}
/**
* 二叉树结构,包含了指向其父节点的指针
*/
public static class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
}
09 用两个栈实现队列
import java.util.Stack;
/**
* 09 用两个栈实现队列
* 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型
* 栈1的push对应队列的deleteHead操作,栈2的pop操作对应队列的insertTail操作,当栈2为空时, 将栈1的元素依次出栈,进到栈2中,如果栈1也为空,则提示队列为空
*/
public class _09 {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
/**
* 队列的insertTail操作
* @param node 插入队尾的元素
*/
public void push(int node) {
stack1.push(node);
}
/**
* 队列的deleteHead操作
* @return 删除队列头部
*/
public int pop() {
if (stack2.empty()) {
if (stack1.empty()) {
throw new RuntimeException("队列为空");
} else {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
}
return stack2.pop();
}
}
10 斐波那契数列
/**
* 10 斐波那契数列
* 大家都知道斐波那契数列,后一项等于前两项和,现在要求输入一个整数n,请你输出斐波那契数列的 第n项(从0开始,第0项为0,n<=39)。
* 使用两个数将结果前两项缓存即可
*/
public class _10 {
public static int Fibonacci(int n) {
int result = 0;
int temp1 = 0;
int temp2 = 1;
if (n == 1) {
return temp2;
}
for (int i = 1; i < n; i++) {
result = temp1 + temp2;
temp1 = temp2;
temp2 = result;
}
return result;
}
public static void main(String[] args) {
System.out.println("Fibonacci(3) = " + Fibonacci(3));
}
}