丑数
描述
给你一个整数 n ,请你找出并返回第 n 个 丑数 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。
实例
示例 1:
输入:n = 10
输出:12
解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。
示例 2:
输入:n = 1
输出:1
解释:1 通常被视为丑数。
解法
小顶堆
要得到从小到大的第 n 个丑数,可以使用最小堆实现。
初始时堆为空。首先将最小的丑数 1 加入堆。
每次取出堆顶元素 x,则 x 是堆中最小的丑数,由于 2x, 3x, 5x 也是丑数,因此将 2x, 3x, 5x加入堆。
上述做法会导致堆中出现重复元素的情况。为了避免重复元素,可以使用哈希集合去重,避免相同元素多次加入堆。
在排除重复元素的情况下,第 n次从最小堆中取出的元素即为第 n个丑数。
class Solution {
public int nthUglyNumber(int n) {
int[] factors = {2,3,5};
Set<Long> seen = new HashSet<Long>();
Queue<Long> heap = new PriorityQueue<Long>();
seen.add(1L);
heap.offer(1L);
int ug = 0;
for (int i = 0; i < n; i++) {
long cur = heap.poll();
ug = (int)cur;
for (int f:factors) {
long next = cur*f;
if (seen.add(next)){
heap.offer(next);
}
}
}
return ug;
}
}
class Solution:
def nthUglyNumber(self, n: int) -> int:
f = [2,3,5]
seen = set()
seen.add(1)
hq = [1]
heapq.heapify(hq)
ug = 0
for i in range(n):
ug = heapq.heappop(hq)
for k in f:
if k*ug not in seen :
seen.add(k*ug)
heapq.heappush(hq,k*ug)
return ug
动态规划
定义数组的dp[],dp[i]表示第i个丑数,第n个丑数就是dp[n];
如何保证dp[i]是大于dp[i-1]的最小丑数;
初始化dp[1] = 1;
设置指针p2,p3,p5,表示下一个丑数是当前指针指向的丑数乘以对应的质因数,三个指针初始值都是1;
状态转移方程:
当2<=i<=n时,dp[i]=min(dp[p2] *2,dp[p3] *3,dp[p5] *5),
然后分别比较dp[i]与dp[p2],dp[p3],dp[p5],如果相等则对应指针加1;
注:判断时候要注意一下,三个比较的数有可能相等,相等的话指针都要加1;
class Solution {
public int nthUglyNumber(int n) {
int [] dp = new int[n+1];
dp[1] = 1;
int p2 = 1,p3=1,p5 = 1;
for (int i = 2; i < n+1; i++) {
int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
dp[i] = Math.min(Math.min(num2, num3), num5);
if (dp[i]==num2){
p2 += 1;
}
if (dp[i]==num3){
p3 += 1;
}
if (dp[i]==num5){
p5 +=1;
}
}
return dp[n];
}
}
链表求和-反向存放
描述
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
解法
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = new ListNode(0);
ListNode prev = head;
int carry = 0;
while(l1!=null ||l2!=null||carry!=0){
int sum = (l1!=null?l1.val:0)+(l2!=null?l2.val:0)+carry;
carry = sum/10;
prev.next = new ListNode(sum%10);
prev = prev.next;
l1 = (l1!=null?l1.next:l1);
l2 = (l2!=null?l2.next:l2);
}
return head.next;
}
}
链表求和-正向存放
描述
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是正向存放的,也就是个位排在链表尾部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例
输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912
解法
利用栈求解
反转链表
描述
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode next = null;
ListNode pre = null;
while(head!=null){
//保存要反转到头的结点
next = head.next;
//
head.next = pre;
pre = head;
head = next;
}
return pre;
}
}
岛屿数量
描述
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
用例
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
岛屿数量
描述
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
思路:
为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 11,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 11 都会被重新标记为 00。
最终岛屿的数量就是我们进行深度优先搜索的次数。
class Solution {
public void dfs(char[][]grid,int row,int col){
int nr = grid.length;
int nc = grid[0].length;
//遍历终止条件
if (row<0||col<0||row>=nr||col>=nc||grid[row][col]=='0'){
return ;
}
//扩散
grid[row][col] = '0';
//以坐标row,col为准想四周遍历
dfs(grid,row-1,col);
dfs(grid,row+1,col);
dfs(grid,row,col-1);
dfs(grid,row,col+1);
}
public int numIslands(char[][] grid) {
if(grid==null||grid.length==0){
return 0;
}
//行数
int nr = grid.length;
//列数
int nc = grid[0].length;
int island_num = 0;
for (int i = 0; i < nr; i++) {
for (int j = 0;j<nc;j++){
if(grid[i][j]=='1'){
++island_num;
dfs(grid,i,j);
}
}
}
return island_num;
}
}
岛屿的周长
描述
给定一个 row x col 的二维网格地图 grid ,其中:grid[i] [j]= 1 表示陆地, grid[i] [j]= 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
示例
示例1:
输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边
示例 2:
输入:grid = [[1]]
输出:4
示例 3:
输入:grid = [[1,0]]
输出:4
思路
迭代
对于一个陆地格子的每条边,它被算作岛屿的周长当且仅当这条边为网格的边界或者相邻的另一个格子为水域。 因此,我们可以遍历每个陆地格子,看其四个方向是否为边界或者水域,如果是,将这条边的贡献(即 11)加入答案 ans 中即可。
class Solution {
static int[] dx = {0,1,0,-1};
static int[] dy ={1,0,-1,0};
public int islandPerimeter(int[][]grid){
int n = grid.length,m=grid[0].length;
int res = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j]==1){
for (int k = 0; k < 4; k++) {
int tx = i + dx[k];
int ty = j + dy[k];
if (ty<0||tx<0||tx>=n||ty>=m||grid[tx][ty]==0){
res += 1;
}
}
}
}
}
return res;
}
}
封闭岛屿的数目
描述
有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 )。
我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座「岛屿」。
如果一座岛屿 完全 由水域包围,即陆地边缘上下左右所有相邻区域都是水域,那么我们将其称为 「封闭岛屿」。
请返回封闭岛屿的数目。
示例
思路
class Solution {
int m;
int n;
int[][] grid;
int count = 0;
public int closedIsland(int[][] grid) {
this.m = grid.length;
this.n = grid[0].length;
this.grid = grid;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j] == 0) {
if(DFS(i,j)) count++;
}
}
}
return count;
}
private boolean DFS(int i,int j){
if(i<0 || j<0 || i>=m || j>=n) return false;
if(grid[i][j]==0){
grid[i][j] = 1;
boolean up = DFS(i-1,j);
boolean down = DFS(i+1,j);
boolean left = DFS(i,j-1);
boolean right = DFS(i,j+1);
return (up && down && left && right);
}
return true;
}
}
链表中倒数第k个节点
描述
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
思路
设置快慢指针来做。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
if (head==null||k==0){
return null;
}
ListNode fast = head;
ListNode slow = head;
int i = 0;
while (i<k && fast!=null){
i++;
fast = fast.next;
}
while (fast!=null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
合并两个排序的链表
描述
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0),cur = dummy;
while (l1!=null && l2!=null){
if (l1.val<l2.val){
cur.next = l1;
l1 = l1.next;
}
else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1!=null?l1:l2;
return dummy.next;
}
}