给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2skh7/
来源:力扣(LeetCode)
当数值大于数组长度时,k的值必须使用 k = k % length
本次采用的是反转数组,分为三部,前面全部、左边和右边
我写的反装函数是这样的
int temp = 0;
int i = start;
int j = end;
for( ; i < j ; i++ , j --){
temp = nums[i] ;
nums[i] = nums[j];
nums[j] = temp;
}
别人写的是这样的
while (start < end) {
int temp = nums[start];
nums[start++] = nums[end];
nums[end--] = temp;
}
原理是采用双指针,和i++先赋值后++的原理
总体代码
class Solution {
public void rotate(int[] nums, int k) {
int length = nums.length-1;
k %= length;
reverse(nums,0,length);
reverse(nums,0,k-1);
reverse(nums,k,length);
}
public void reverse(int[] nums, int start, int end){
int temp = 0;
int i = start;
int j = end;
for( ; i < j ; i++ , j --){
temp = nums[i] ;
nums[i] = nums[j];
nums[j] = temp;
}
while (start < end) {
int temp = nums[start];
nums[start++] = nums[end];
nums[end--] = temp;
}
}
}
另外一种做法是
利用 k = k % length 去获取每个值旋转后的位置,然后利用一个新的数组去存取。
给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
利用两种方式去解决,
第一种是暴力解法,但是会导致算法直接超时间。
所以可以使用先排序,因为排序后数组顺序是有序的。
第二种解法是利用Set解法,采用Set解法之后,因为add方法会对重复元素返回false
import java.util.*;
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
for(int i = 0; i < nums.length ; i++){
if(!set.add(nums[i])){
return true;
}
}
return false;
}
}
总结:在思考问题时候可以考虑一些集合框架和数据结构能否对应。
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
使用异或 (我也是第一次接触到这样)
1 ^ 0 = 1
0 ^ 0 = 0
而且通过断点后是可以满足交换律的
a ^ b ^ a = a ^ a ^ b
class Solution {
public int singleNumber(int[] nums) {
int result = 0 ;
for(int num : nums){
result ^= num;
}
return result;
}
}
两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。
我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
坏味道
int i,j = 0;是不行的
int i =0 ,j = 0;是可行的
ArrayList 只能存放Integer类型,故其toArray()方式不适合转换为int。最后需要手动转换为数组。
思路:
这道题的思路依然是先排序,发现一个道理,好像数组问题,都是排序后好解决很多。
import java.util.*;
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
List<Integer> result = new ArrayList<>();
int i = 0,j = 0;
while(i<nums1.length && j < nums2.length){
if(nums1[i] == nums2[j]){
result.add(nums1[i]);
i++;
j++;
}else{
boolean jude = nums1[i] > nums2[j];
i = jude ? i : ++i;
j = jude ? ++j : j;
}
}
int[] resultToArray = new int[result.size()];
int z = 0;
for(int nums : result){
resultToArray[z++] = nums;
}
return resultToArray;
}
}
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2ba4i/
来源:力扣(LeetCode)
解题思路,利用双指针
自己的方法
class Solution {
public void moveZeroes(int[] nums) {
int fast = 1;
int low = 0;
while(fast < nums.length){
if(nums[low] == 0 && nums[low] != nums[fast] ){
int temp = nums[low];
nums[low++] = nums[fast];
nums[fast++] = temp;
}else if(nums[low] == 0 && nums[low] == nums[fast] ) {
fast ++ ;
}else{
fast ++ ;
low ++ ;
}
}
}
}
别人的方法,是采用不为零的往前移动。
public void moveZeroes(int[] nums) {
if (nums == null || nums.length == 0)
return;
int index = 0;
//一次遍历,把非零的都往前挪
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0)
nums[index++] = nums[i];
}
//后面的都是0,
while (index < nums.length) {
nums[index++] = 0;
}
}
作者:数据结构和算法
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2ba4i/?discussion=AJ2rEF
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
示例 1:
输入:board =
[[“5”,“3”,".",".",“7”,".",".",".","."]
,[“6”,".",".",“1”,“9”,“5”,".",".","."]
,[".",“9”,“8”,".",".",".",".",“6”,"."]
,[“8”,".",".",".",“6”,".",".",".",“3”]
,[“4”,".",".",“8”,".",“3”,".",".",“1”]
,[“7”,".",".",".",“2”,".",".",".",“6”]
,[".",“6”,".",".",".",".",“2”,“8”,"."]
,[".",".",".",“4”,“1”,“9”,".",".",“5”]
,[".",".",".",".",“8”,".",".",“7”,“9”]]
输出:true
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2f9gg/
来源:力扣(LeetCode)
利用三个二维数组去存放行,列,九宫格,通过值的大小进行存放位置。
class Solution {
public boolean isValidSudoku(char[][] board) {
int length = board.length;
int[][] line = new int[length][length];
int[][] colunm = new int[length][length];
int[][] cell = new int[length][length];
for(int i = 0 ; i < length; i++){
for(int j = 0 ; j < length; j++){
if(board[i][j] == '.'){
continue;
}
int num = board[i][j] - '0' - 1;
int k = i / 3 * 3 + j / 3;
if(line[i][num] != 0 || colunm[j][num] != 0 || cell[k][num] != 0){
return false;
}
line[i][num] = colunm[j][num] = cell[k][num] = 1;
}
}
return true;
}
}
不懂的是
int num = board[i][j] - ‘0’ - 1;
int k = i / 3 * 3 + j / 3; 确定九宫格的位数
旋转图像
旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xnhhkv/
来源:力扣(LeetCode)
lass Solution {
public void rotate(int[][] matrix) {
int length = matrix.length;
for(int i = 0 ; i < length / 2 ; i++){
int[] temp = new int[length];
temp = matrix[i];
matrix[i] = matrix[length - i - 1];
matrix[length - i - 1] = temp;
}
for(int i = 0 ; i < length ; i++){
for(int j = i + 1 ; j < length ; j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
}
}
先首行和尾行进行反转,再对角线替换,总体而言,更重要的是需要查看题目的规律,主要还是找不到突破点。
或者从外圈到内圈里面慢慢遍历。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false
使用双指针的方法,从左从右判断,先去除非字符串,可以使用正则或者Chacater.isLetterOrDigit()方法判断
class Solution {
public boolean isPalindrome(String s) {
// 不会转小写
s = s.toLowerCase();
int j = s.length() - 1;
int i = 0 ;
while(i < j){
// 没有进行多个遍历
while(i < j && !Character.isLetterOrDigit(s.charAt(j))){
j--;
}
while(i < j && !Character.isLetterOrDigit(s.charAt(i))){
i++;
}
if(s.charAt(i) != s.charAt(j)){
return false;
}
i ++ ;
j -- ;
}
return true;
}
// 判断字符串里的字符是否会是火星文
// public boolean judege (char start){
// if(start == ',' || start == ':' || start == ' ' || start == '.' || start == '@'){
// return true;
// }else{
// return false;
// }
// }
}
输入:n = 4
输出:“1211”
解释:
countAndSay(1) = “1”
countAndSay(2) = 读 “1” = 一 个 1 = “11”
countAndSay(3) = 读 “11” = 二 个 1 = “21”
countAndSay(4) = 读 “21” = 一 个 2 + 一 个 1 = “12” + “11” = “1211”
class Solution {
public String countAndSay(int n) {
if(n == 1){
return "1";
}
int count = 0;
String belen = countAndSay(n-1);
char local = belen.charAt(0);
StringBuilder res = new StringBuilder();
for(int i = 0 ; i < belen.length() ; i++){
if(belen.charAt(i) == local){
count ++ ;
}else{
res.append(count);
res.append(local);
local = belen.charAt(i);
count = 1;
}
}
res.append(count);
res.append(local);
return res.toString();
}
}
采用计数到遍历。
总结,可以采用画图法来探讨。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode node = head;
int lengt = 0;
// 计数
while(node != null){
lengt ++ ;
node = node.next;
}
int dingwei = lengt - n ;
if(dingwei == 0){
return head.next;
}
// 保留前一个链表
ListNode nodes = head;
for(int i = 0 ; i < dingwei -1 ; i++){
nodes = nodes.next;
}
nodes.next = nodes.next.next;
return head;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if( null == head || null == head.next ){
return head;
}
ListNode node = head.next;
ListNode pre = head;
int count = 0;
while(node.next != null){
node = node.next;
pre = node;
count ++ ;
}
ListNode pres = reverseTwo(pre,node);
if(count == 0){
return node;
}
count ++;
while(count != 0 ){
ListNode prea = head;
while(prea.next != pres ){
prea = prea.next;
}
pres = reverseTwo(prea,pres);
count -- ;
}
return node;
}
public ListNode reverseTwo(ListNode pre,ListNode now){
now.next = pre;
pre.next = null;
return pre;
}
}
通过先反转倒数两个,然后遍历每次从头节点到pre节点进行转换。利用count计数器计数。
其余方法
利用栈
利用双链表,每一次进行插入头节点。
请判断一个链表是否为回文链表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
Stack<Integer> stack = new Stack<Integer>();
ListNode node = head;
while(node != null){
stack.push(node.val);
node = node.next;
}
while(head.next != null){
if(stack.pop() == head.val){
head = head.next;
}else{
return false;
}
}
return true;
}
}
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
采用集合的算法。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode node = head;
Set<ListNode> set = new HashSet<>();
while(node.next != null){
if(!set.add(node)){
return true;
}
node = node.next;
}
return false;
}
}
采用快慢指针
就像钟一样,对于时针,秒来说,快慢环形,会达到接触,只要判断两个快慢指针是否能够相等即可。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode fast = head;
ListNode slow = head;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
}
二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
return root==null? 0 : Math.max(maxDepth(root.left), maxDepth(root.right))+1;
}
}
验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root,Long.MIN_VALUE,Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long minVal,long maxVal){
if(root == null ){
return true;
}
if(root.val <= minVal || root.val >= maxVal){
return false;
}
return isValidBST(root.left,minVal,root.val) && isValidBST(root.right, root.val,maxVal);
}
}