文章目录
1. 搜索旋转排序数组 II
链接:https://leetcode.com/problems/search-in-rotated-sorted-array-ii
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3输出: false
分析:
注意看这个数组,一开始是升序排序的,然后旋转,这个时候我们把它看做两部分,两个部分都是升序排序的,为了方便描述,我就用 a b c d来代表这两个部分,它们的大小是这样的c < d < a < b(忽略等号),然后我们就可以依照这个大小顺序来与target进行比较了,具体的在代码里面有详细注释
public boolean search(int[] nums, int target) {
int i = nums.length;
// 判断数组为空的情况
if (i == 0){
return false;
}
// 首先判断队首和队尾
if (nums[0] == target || nums[i-1] == target){
return true;
}
// 现在从队首开始
if (target > nums[0]){
for (int i1 = 1; i1 < nums.length; i1++) {
// 如果比nums[0]小,说明已经到了第二队
if (nums[0] > nums[i1]){
return false;
}
if (target == nums[i1]){
return true;
}
}
}
// 现在开始从队尾比较
if (target < nums[i-1]){
for (int i1 = nums.length - 2; i1 >= 0; i1--) {
// 说明已经到了第一对
if (nums[i-1] < nums[i1]){
return false;
}
if (target == nums[i1]){
return true;
}
}
}
return false;
}
2. 汉诺塔问题
汉诺塔是一个数学谜题。有3根柱子(或木桩、塔)和一些可以在柱子之间来回移动的不同大小的圆盘。开始时,所有的圆盘按照从小到大的次序自上而下叠放在一根柱子上,形成一个圆锥结构。现在要求把整叠圆盘移动到另一根柱子上,移动时要遵守下面的规则:
每次只能移动一个圆盘。
每次移动,只能移动柱子最上面的一个圆盘到另一根柱子(这根柱子上有可能已有圆盘)。
任何时候不能出现大圆盘在小圆盘上方的情况。
分析:
使用递归的方法来解决,递归的两种情形:递归情形+基本情形(终止条件)
- 当只有一个盘子的时候,直接移动,可以作为递归的基本情形
- 当有两个盘子时,假如为1、2,柱子为A、B、C,那么移动情况就可以是1 --> B,2 --> C,1 --> C,可以作为递归的递归情形
综上,我们把所有的盘子,只看做n
和n-1
,那么依据第二点的分析来看有以下代码
public class TowersOfHanoi {
private static void fact(int n, char from, char to, char middle) {
// 递归的基本情形
if (n == 1) {
System.out.println("将disk1从柱子" + from + "移到柱子" + to);
}else {
// 注意看这三条语句,分别对应上面第二点分析的移动情况,n-1需要移动两次,而且它需要调用递归,而n只需要移动一次,所以直接打印
fact(n - 1, from, middle, to);
System.out.println("将disk" + n + "从柱子" + from + "移到柱子" + to);
fact(n - 1, middle, to, from);
}
}
public static void main(String[] args) {
char a = 'a';
char b = 'b';
char c = 'c';
fact(3, a, b, c);
}
}
3. 非递归中序遍历二叉树
唉,这个是今天的一个痛啊,因为构造方法没有弄明白而导致浪费了很多时间,还好最后还是找出了问题是出在了哪里,不过还是没有搞清楚为什么那种方式会出错,以后再慢慢弄清楚吧
回到这个题,我觉得主要是两个地方吧:
-
二叉树的赋值问题
-
二叉树的非递归问题
其实之前在学C语言的时候,给二叉树赋值我就觉得有点难,现在用java,本来想去找一下以前的笔记,想想两个语言还是有差别的,就只能硬着头皮写了,然后其实中序遍历二叉树使用递归方法还是很简单的,包括三种遍历方式,无外乎就是执行语句换一个顺序而已,但是非递归的话就要难一点了
以上两个问题的解决: -
使用一个二叉树类型的list,先将所有目标元素变为一个节点,存进去,然后利用一个循环,将list里面的元素加入二叉树里面去
-
使用一个二叉树类型的栈,保存右孩子,依照中序遍历的特点,遍历至最左下的节点,然后进行输出,具体的看代码
public class DemoTree {
private int data;
private DemoTree left;
private DemoTree right;
public DemoTree(int data) {
this.data = data;
}
public DemoTree() {
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DemoTree getLeft() {
return left;
}
public void setLeft(DemoTree left) {
this.left = left;
}
public DemoTree getRight() {
return right;
}
public void setRight(DemoTree right) {
this.right = right;
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class DemoTreeTest {
private static DemoTree tree = new DemoTree();
// 给二叉树赋值
private static void create12(int[] obj){
List<DemoTree> list = new ArrayList<>();
for (int i : obj) {
list.add(new DemoTree(i));
}
tree = list.get(0);
for (int i = 0; i < obj.length / 2; i++) {
list.get(i).setLeft(list.get(i * 2 + 1));
if (i * 2 + 2 < obj.length) {
list.get(i).setRight(list.get(i * 2 + 2));
}
}
}
// 非递归中序遍历
private static void fact(DemoTree root) {
// 使用栈来保存左孩子
Stack<DemoTree> stack = new Stack<>();
if (root == null){
return;
}
while (true){
while (root != null){
stack.push(root);
root = root.getLeft();
}
if (stack.isEmpty()){
break;
}else {
root = stack.pop();
System.out.print(root.getData() + " ");
root = root.getRight();
}
}
}
public static void main(String[] args) {
int[] objs = {1, 2, 3, 4, 5, 6, 7};
create12(objs);
fact(tree);
}
}
4. 字符串的相关操作(stringbuilder、stringbuffer、stringTokenizer等)
1.反转字符串,一开始本来使用的是stringbuffer,但是后来发现想到stringbuilder效率要高一点,因为stringbuffer是线程安全的,安全虽好,但是降低了它的效率
private static String reverse(String str) {
int i, len = str.length();
StringBuilder buffer = new StringBuilder(len);
for (i = len - 1; i >= 0; i--) {
buffer.append(str.charAt(i));
}
return buffer.toString();
}
2.反转一个句子,这里只演示以空格进行分割的句子,分割的话就使用StringTokenizer来进行操作,然后对应分割之后的拼接,一开始是想到直接以string类型进行"+"拼接,但是这种方式会导致一些内存浪费等不好的一些,所以还是建议使用StringBuilder 来进行操作
private static String reversSentence(String line){
// 指定空格为分隔符
StringTokenizer tokenizer = new StringTokenizer(line," ");
StringBuilder str = new StringBuilder();
while (tokenizer.hasMoreTokens()){
// 因为要反转,所以直接在0位置进行插入即可
// 先插入一个空格,相当于还原它本来的空格
str.insert(0," " );
str.insert(0, tokenizer.nextToken());
}
return str.toString();
}
3.找出给定字符集的所有可能排列序列,这里主要用到递归,递归这个东西吧,有时候好理解,有时候又搞不懂,还是得多练,特别注意递归的条件和参数,比如说这里,for循环里面传的是list.size()
,而这个大小是在每一次递归都会改变的,而正是因为这个改变,才能让prefix
组合为一个可能的排序进而输出,然后最开始是直接给prefix
赋空值的
private static void palindrome(List<String> list, String prefix, int length) {
if (prefix.length() == length) {
System.out.println(prefix);
}
for (int i = 0; i < list.size(); i++) {
List<String> tmp = new LinkedList<String>(list);
palindrome(tmp, prefix + tmp.remove(i), length);
}
}
public static void main(String[] args) {
String[] array = new String[]{"a", "b", "c","d"};
// 第一个参数是list类型,第二个参数直接传空字符串,第三个参数就是list的长度
palindrome(Arrays.asList(array), "", array.length);
}
5.找出只出现一次的数字
给定一个非空整数数组,除了某个元素只出现奇数次以外,其余每个元素均出现偶数次。找出那个只出现了奇数次的元素。要求具有线性时间复杂度,且不使用额外空间
分析:如果没有后面的要求的话,使用map来存储元素,是很简单的,但是要求不使用额外空间,就有点麻烦了,这里我介绍使用异或这种方法:a⊕b⊕a=(a⊕a)⊕b=0⊕b=b
,然后偶数次的两两抵消,奇数次的最后只剩下一次
public class FindOnlyOne {
private static int findOnlyOne(int[] nums){
int ans = nums[0];
for (int i = 1; i < nums.length; i++) {
ans = ans ^ nums[i];
}
return ans;
}
public static void main(String[] args) {
int[] nums = {1,2,3,3,2,1,1};
System.out.println(findOnlyOne(nums));
}
}
6.环形链表
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
public boolean hasCycle(ListNode head) {
Set<ListNode> nodesSeen = new HashSet<>();
while (head != null) {
if (nodesSeen.contains(head)) {
return true;
} else {
nodesSeen.add(head);
}
head = head.next;
}
return false;
}
7.单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
使用记忆化递归,一个HashMap保存之前检索过的串
class Solution{
HashMap<String,Boolean> map = new HashMap<>();
public boolean wordBreak(String s, List<String> wordDict){
return isContains(s,wordDict );
}
public boolean isContains(String s,List<String> wordDict){
if ("".equals(s)){
return true;
}
if(map.containsKey(s)){
return map.get(s);
}
boolean result = false;
for (int i = 0;i < s.length();i++){
if (wordDict.contains(s.substring(0,i+1))){
result = result || isContains(s.substring(i+1),wordDict );
if(!result){
map.put(s,false);
}
}
}
return result;
}
}