LeetCode3.22-3.23

面试题28. 对称的二叉树

题目:
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

解法1:递归。每次判断两个镜像位置节点的值是否相等,递归实现。

public boolean isSymmetric(TreeNode root) {
		if (root == null)
			return true;
		return isMirror(root.left, root.right);
	}

	public boolean isMirror(TreeNode left, TreeNode right) {
		if (left == null && right == null)
			return true;
		if (left == null || right == null)
			return false;
		if (left.val == right.val)
			return isMirror(left.left, right.right) && isMirror(left.right, right.left);
		return false;
	}

解法2:迭代。使用队列,类似于BFS,但每次存储树的两个镜像节点,队列中每两个相邻的节点值应该相等,若不相等返回false。

public boolean isSymmetric1(TreeNode root) {
		/*
		 * 类似于BFS,每次存入两个节点,第一颗树从左向右存储,第二棵树从右向左存储,队列中每两个相邻的值应该是相等的
		 * 每次取出两个值比较,若不相等返回false
		 * 两棵树比较完毕,返回true
		 */
		if(root == null) return true;
		Queue<TreeNode> queue = new LinkedList<>();
		//队列中初始放入两个根节点
		queue.add(root);
		queue.add(root);
		while(!queue.isEmpty()) {
			//每次取出两个节点
			TreeNode node1 = queue.poll();
			TreeNode node2 = queue.poll();
			if(node1 == null && node2 == null) 
				continue;
			if(node1 == null || node2 == null) 
				return false;
			if(node1.val != node2.val)
				return false;
			//
			queue.add(node1.left);
			queue.add(node2.right);
			queue.add(node1.right);
			queue.add(node2.left);
		}
		return true;
	}

3.22每日一题

题目
给定整数数组 A,每次 move 操作将会选择任意 A[i],并将其递增 1。

返回使 A 中的每个值都是唯一的最少操作次数。

示例 1:
输入:[1,2,2]
输出:1
解释:经过一次 move 操作,数组将变为 [1, 2, 3]。
示例 2:
输入:[3,2,1,2,1,7]
输出:6
解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7]。
可以看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。

提示:
0 <= A.length <= 40000
0 <= A[i] < 40000

解法1:
先给数组排序,遍历数组,如果当前元素小于等于前一个元素,当前元素变为前一个元素+1,对操作数的贡献为:前一个元素+1 - 当前元素

public class Solution {
    public int minIncrementForUnique(int[] A) {
    	//数组排序
    	quickSort(A, 0, A.length-1);
    	int ans = 0;
    	//如果当前元素比前一个元素小,那么当前元素变为前一个元素+1,操作数 += A[i]-pre
    	for(int i = 1; i < A.length; i++) {
    		if(A[i] <= A[i-1]) {
    			int pre = A[i];
    			A[i] = A[i-1] + 1;
    			ans += A[i] - pre;
    		}
    	}
    	return ans;
    }
    
    public void quickSort(int[] arr, int left, int right) {
    	if(left >= right) return;
    	int i = left;
    	int j = right;
    	int base = arr[i];
    	while(i < j) {
    		//移动右指针
    		while(arr[j]>=base && i<j) {
    			j--;
    		}
    		//移动左值针
    		while(arr[i]<=base && i<j) {
    			i++;
    		}
    		if(i<j) {
    			//交换i和j处的值
    			int temp = arr[i];
    			arr[i] = arr[j];
    			arr[j] = temp;
    		}
    	}
    	//本轮遍历完成
    	arr[left] = arr[i];
    	arr[i] = base;
    	quickSort(arr, left, i-1);
    	quickSort(arr, i+1, right);
    }
}

解法2:
用一个数组记录每个数字出现的次数,使用一个int值记录数组中的最大值。

当我们找到一个没有出现过的数的时候,将之前某个重复出现的数增加成这个没有出现过的数。注意,这里 「之前某个重复出现的数」 是可以任意选择的,它并不会影响最终的答案,因为将 P 增加到 X 并且将 Q 增加到 Y,与将 P 增加到 Y 并且将 Q 增加到 X 都需要进行 (X + Y) - (P + Q) 次操作。

例如当数组 A 为 [1, 1, 1, 1, 3, 5] 时,我们发现有 3 个重复的 1,且没有出现过 2,4 和 6,因此一共需要进行 (2 + 4 + 6) - (1 + 1 + 1) = 9 次操作。

算法

首先统计出每个数出现的次数,然后从小到大遍历每个数 x:
如果 x 出现了两次以上,就将额外出现的数记录下来(例如保存到一个列表中);
如果 x 没有出现过,那么在记录下来的数中选取一个 v,将它增加到 x,需要进行的操作次数为 x - v。
我们还可以对该算法进行优化,使得我们不需要将额外出现的数记录下来。还是以 [1, 1, 1, 1, 3, 5] 为例,当我们发现有 3 个重复的 1 时,我们先将操作次数减去 1 + 1 + 1。接下来,当我们发现 2,4 和 6 都没有出现过时,我们依次将操作次数增加 2,4 和 6。

注意事项

虽然 A[i] 的范围为 [0, 40000),但我们有可能会将数据递增到 40000 的两倍 80000。这是因为在最坏情况下,数组 A 中有 40000 个 40000,这样要使得数组值唯一,需要将其递增为 [40000, 40001, …, 79999],因此用来统计的数组需要开到A.max+40000。当遍历到数组最大值且没有多余的数字时,遍历即可终止。

 public int minIncrementForUnique(int[] A) {
		 int[] count = new int[A.length+40000];
		 //记录A中最大的数
		 int max = 0;
		 //操作次数
		 int ans = 0;
		 //重复数字的个数
		 int taken = 0;
		 
		 //每个数出现几次
		 for(int num : A) {
			 count[num]++;
			 max = Math.max(num, max);
		 }
		 
		 for(int i = 0; i < count.length; i++) {
			 if(count[i] > 1) {
				 //如果该数字是重复的,taken记录该数字多于1的个数
				 taken += count[i]-1;
				 ans -= i*(count[i]-1);
			 }else if(count[i] == 0 && taken > 0){
				 //如果该数字没出现过且有多余的数字
				 taken--;
				 ans += i;
			 }
			 //如果当前数字是最大值,且没有多余的数字,那么直接返回即可
			 if(i == max && taken == 0) {
				 return ans;
			 }
		 }
		 return ans;
	 }

3.23 每日一题

题目:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

解法1:
先遍历一次,记录链表长度N,下一次遍历到第N/2+1个节点,该节点为目标节点。

public ListNode middleNode(ListNode head) {
		if(head.next == null) return head;
		int len = 0;
		ListNode pre = new ListNode(-1);
		pre.next = head;
		while(head != null) {
			len++;
			head = head.next;
		}
		int mid = (len >>> 1) + 1;
		len = 1;
		head = pre.next;
		while(len < mid) {
			len++;
			head = head.next;
		}
		return head; 
	}

解法2:
快慢指针,快指针fast每次前进两步,慢指针slow每次前进一步,当fast=null或者fast.next==null的时候slow就是中间节点。

public ListNode middleNode(ListNode head) {
		ListNode slow = head;
		ListNode fast = head;
		while(fast != null && fast.next != null) {
			slow = slow.next;
			fast = fast.next.next;
		}
		return slow;
	}

707.设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

提示:

所有val值都在 [1, 1000] 之内。
操作次数将在  [1, 1000] 之内。
请不要使用内置的 LinkedList 库。
class ListNode {
	int val;
	ListNode next;

	ListNode(int x) {
		val = x;
	}
}

class MyLinkedList {
	int size;	//size记录该链表的长度
	ListNode head;
    /** Initialize your data structure here. */
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0); //伪头
    }
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    public int get(int index) {
        if(index >= size || index < 0) {
        	return -1;
        }
        
        ListNode curr = head;
        for(int i = 0; i < index + 1; i++) {
        	curr = curr.next;
        }
        return curr.val;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    public void addAtHead(int val) {
    	addAtIndex(0, val);
    }
    
    /** Append a node of value val to the last element of the linked list. */
    public void addAtTail(int val) {
    	addAtIndex(size, val);
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    public void addAtIndex(int index, int val) {
    	if(index > size) return;
    	ListNode toAdd = new ListNode(val);
    	ListNode curr = head;
    	if(index <= 0) {
    		//在头部插入
    		toAdd.next = head.next;
    		head.next = toAdd;
    	}else{
    		//在链表index位置插入
    		for(int i = 0; i < index; i++) {
    			curr = curr.next;
    		}	
    		toAdd.next = curr.next;
    		curr.next = toAdd;
    	}
		size++;
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    public void deleteAtIndex(int index) {
    	if(index < 0 || index >= size) return;
    	ListNode curr = head;
    	for(int i = 0; i < index; i++) {
    		curr = curr.next;
    	}
    	curr.next = curr.next.next;
    	size--;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值