Leetcode刷题 week 1

  1. 回文链表

请判断一个链表是否为回文链表。

  示例 1:
    
    输入: 1->2
    输出: false
    示例 2:
    
    输入: 1->2->2->1
    输出: true
    进阶:
    你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

    解法1:动态数组 + 双指针思想 时间复杂度O(n),空间复杂度O(n)
    class Solution {
    public:
        bool isPalindrome(ListNode* head) {
            //特判
            if (!head || !head->next) {
                return true;
            }
            //用一个动态数组作为辅助数组
            vector<int> rev;   
            ListNode* first = head;
            while(first) {
                rev.push_back(first->val);
                first = first->next;            
            }
            //然后双指针,一个个对比。
            int l = 0, r = rev.size() - 1;
            while(l < r){
                if(rev[l] != rev[r])
                    return false;
                l++;
                r--;
            }
            return true;
        }
    };
 解法2:反转链表
    class Solution {
    public:
        bool isPalindrome(ListNode* head) {
    		//特判
            if(!head || !head->next) 
                return true;
            //初始化
            ListNode* slow = head;
            ListNode* fast = head;
            // 将slow指针移动到链表中间位置
            // 快指针走两步,慢指针走一步,当快指针走到最后的时候,慢指针只走到了一半。
            while(fast && fast->next){
                slow = slow->next;
                fast = fast->next->next;
            }
            // 反转后半部分
            ListNode* curNode = slow;//当前指针
            ListNode* nextNode = slow->next; 
            while (nextNode) {
                ListNode* tmp = nextNode->next;
                nextNode->next = curNode;
                curNode = nextNode;
                nextNode = tmp;
            }
            slow->next = nullptr;
            // 开始比较是否相等
            while(head && curNode){
                if(head->val != curNode->val)
                    return false;
                head = head->next;
                curNode = curNode->next;
            }
            return true;
        }
    };
  1. 缺失数字
解法1:n的总和 - 那些数的总和 时间复杂度 O(n) 

    class Solution {
    public:
        int missingNumber(vector<int>& nums) {
            int sum = 0;
            for (int i = 0; i < nums.size(); ++i) {
                sum += nums[i];
            }
            return nums.size() * (nums.size() + 1) / 2 - sum;
        }
    };

在这里插入图片描述

在这里插入图片描述

有个小疑问:

为什么Java的运行时间这么短?Why?

为什么C++的运行时间那么长?想知道…

解法2:异或运算 时间复杂度 O(n) 
异或运算法则:相同为0,不同为1
1^1=0   1^0=1   0^1=1   0^0=0 
所以在这里,以这个为例子就是:

3 ( 2 ) 3_{(2)} 3(2) = 0 1 1

0 ( 2 ) 0_{(2)} 0(2) = 0 0 0

1 ( 2 ) 1_{(2)} 1(2) = 0 0 1

可以推出这样一个性质:1 ^ 1 ^ 2 ^ 2 ^ 3 ^ 3 ^ 4=  4; 

数组的下标为 0 ~ n - 1 ,但是数组元素是0 ~ n的,所以只要初始值设置为n,那么最终异或下去,就会得到缺失的那个数。
class Solution {
    public int missingNumber(int[] nums) {
        int res = nums.length;
        for (int i = 0; i < nums.length; ++i) {
            res ^= nums[i];
            res ^= i;
        }
        return res;
    }
}

附上一个过程图:
在这里插入图片描述

关键是要把数组下标和值对应起来 !!

总结: 异或和可以找出一段连续数据缺失的数。

  1. 第一个错误的版本

题目说:尽量减少对调用 API 的次数

解法:二分查找 时间复杂度为 O(logn)

   /* The isBadVersion API is defined in the parent class VersionControl.
          boolean isBadVersion(int version); */
    
    //上面说了已经有一个 boolean isBadVersion() 函数了
    public class Solution extends VersionControl {
        public int firstBadVersion(int n) {
            //二分的关键就是找到 上界
            int l = 0, r = n;
            while (l < r) {
                int mid = (int) (((long) l + r) >> 1);
                if (isBadVersion(mid)) {
                    r = mid;
                } else {
                    l = mid + 1;
                }
            }
            return l;
        }
    }

整数二分模板 ①:使用条件是: 区间 [l, r] 被划分成 [l, mid] 和 [mid + 1, r] 时使用

public int bsearch_1(int l, int r) {
	while (l < r) {
		int mid = l + r >> 1;
		if (check(mid)) // check()判断mid是否满足性质
            r = mid;    
		else 
           	l = mid + 1;
	}
	return l;
}

整数二分模板 ②:使用条件是: 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用

public int bsearch_2(int l, int r) {
	while (l < r)
	{
		int mid = l + r + 1 >> 1;
		if (check(mid)) // check()判断mid是否满足性质
            l = mid;
		else r = mid - 1;
	}
	return l;
}

浮点二分模板 :

public int bsearch_2(int l, int r) {
	double l = 0 , r = x;
	//比如如果题目要求保留4位小数,则应该写到-6次方,比有效小数的位数多2
	while (r - l > 1e-6) {
	//还可以写成 for(int i = 0; i < 100; ++i) 循环100次相当于整个区间/ 2的100次方,最后的结果也是很精确的。
		double mid = (l + r) / 2;//这里就不可以用l + r >> 1;
		if (mid >= x / mid)//防止溢出
			r = mid;
		else
			l = mid;
	}
    return l;
}
  1. 移动零
解法1:冒泡排序 时间复杂度:O(n^2)
解法2:快慢双指针  时间复杂度:O(n)  空间复杂度:O(1)
    
双指针算法,先用快指针遍历一遍数组,当快指针遍历的元素为非零的时候,将该元素赋值给慢指针。存在0的情况,所以一定存在快慢指针。如果不存在0的情况,则直接输出数组即可。
    
class Solution {
    public void moveZeroes(int[] nums) {
        //可写可不写,为了严谨一点还是写上好了
        if (nums.length == 0)
            return;
        int idx = 0;
        for (int i = 0; i < nums.length; ++i) {
            //当快指针的元素遇到非零时,将值赋给慢指针。
            if (nums[i] != 0) {
                nums[idx++] = nums[i];
            }    
        }
        //后续补0
        while (idx < nums.length) {
            nums[idx++] = 0;
        }
    }
}
  1. 单词规律

  2. Nim 游戏 (尼姆博弈)

你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 ~ 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。

class Solution {
public:
    bool canWinNim(int n) {
        return n % 4;
    }
};

改一下题目,将每次你们轮流拿掉 1 ~ 3 块石头 ,改成每次你们轮流拿掉 1 ~ m 块石头

总是给对手留下 m + 1 块石头,那么对手一定会输。

解法1%
class Solution {
public:
    bool canWinNim(int n) {
        return n % (m + 1);
    }
};



解法2&
class Solution {
public:
    bool canWinNim(int n) {
        return n & m;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值