日期: 2021 年 6 月 2 日

IOS中关于Get请求带中文参数问题

通常后台给我们的接口,如果是Get请求是比较少带参的,而IOS中必须把URL中含有的中文字符转化为UTF8编码,String 为转换后我们所需的URL地址。

例如:
预留参数的网址
NSString *Str = @”https://api.smartnlp.cn/cloud/answer?q=你好”;
将网址转化为UTF8编码
NSString *String = [Str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
1
2
3
4
你会发现 stringByAddingPercentEscapesUsingEncoding 方法出现了感叹号,那是因为iOS9.0后,该方法已经被另一个方法替代了

将网址转化为UTF8编码
NSString *String = [Str stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSe

iOS13关闭应用暗黑模式,适配暗黑模式

为了让APP无论是在浅色还是深色模式下,App的UI保持不变

1、全局

在info.plist里面加一个key:UIUserInterfaceStyle,把它的值设为Light

2、单个页面不支持

if #available(iOS 13.0, *) {
self.overrideUserInterfaceStyle = .light
} else {
// Fallback on earlier versions
}
3、根据用户的选择进行暗黑模式适配

extension UIColor {
convenience init(light: UIColor, dark: UIColor) {
self.init(dynamicProvider: { (traits) in
if traits.userInterfaceStyle == .dark {
return dark
}
return light
})
}
}

iOS13-适配夜间模式/深色外观(Dark Mode)

今天的 WWDC 19 上发布了 iOS 13,我们来看下如何适配 DarkMode。

 

首先我们来看下效果图

 

效果图.gif

 

DarkMode 主要从两个方面去适配,颜色和图片
颜色适配

 

iOS 13 下 UIColor  增加了一个初始化方法,我们可以用这个初始化方法完成 DarkMode 的适配

 

@available(iOS 13.0, *)
public init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)
这个方法要求传一个闭包进去,当系统从 LightMode 和 DarkMode 之间切换的时候就会触发这个回调。
这个闭包返回一个 UITraitCollection 类,我们要用这个类的 userInterfaceStyle 属性。
userInterfaceStyle 是一个枚举,声明如下

 

@available(iOS 12.0, *)
public enum UIUserInterfaceStyle : Int {
case unspecified
case light
case dark
}

这个枚举会告诉我们当前是 LightMode or DarkMode

 

 

现在我们创建两个 UIColor 并赋值给view.backgroundColor 和 label,代码如下

 

let backgroundColor = UIColor { (traitCollection) -> UIColor in
switch traitCollection.userInterfaceStyle {
case .light:
return UIColor.white
case .dark:
return UIColor.black
default:
fatalError()
}
}
view.backgroundColor = backgroundColor

let labelColor = UIColor { (traitCollection) -> UIColor in
switch traitCollection.userInterfaceStyle {
case .light:
return UIColor.black
case .dark:
return UIColor.white
default:
fatalError()
}
}
label.textColor = labelColor

现在,我们做完了背景色和文本颜色的适配,接下来我们看看图片如何适配

 

图片适配

 

打开 Assets.xcassets

把图片拖拽进去,我们可以看到这样的页面

 

 

然后我们在右侧工具栏中点击*后一栏,点击 Appearances 选择 Any, Dark,如图所示

 

 

我们把 DarkMode 的图片拖进去,如图所示

 

 

*后我们加上 ImageView 的代码

 

imageView.image = UIImage(named: “icon”)
现在我们就已经完成颜色和图片的 DarkMode 适配,是不是很简单呢 (手动滑稽)

 

如何获取当前样式 (Light or Dark)
我们可以看到,不管是颜色还是图片,适配都是系统完成的,我们不用关心现在是什么样的样式。
但是在某些场景下,我们可能会有根据当前样式来做一些其他适配的需求,这时我们就需要知道现在什么样式。
我们可以在 UIViewController 或 UIView 中调用 traitCollection.userInterfaceStyle来获取当前样式,代码如下

 

 

switch traitCollection.userInterfaceStyle {
case .unspecified:
print(“unspecified”)
case .light:
print(“light”)
case .dark:
print(“dark”)
}
为什么要强调当前呢,因为默认情况下使用 traitCollection.userInterfaceStyle 属性就能获取到当前系统的样式。
但是我们可以通过 overrideUserInterfaceStyle 属性强制设置 UIViewController 或 UIView 的样式,代码如下

 

overrideUserInterfaceStyle = .dark
print(traitCollection.userInterfaceStyle)  // dark

我们可以看到设置了 overrideUserInterfaceStyle 之后,traitCollection.userInterfaceStyle 就是设置后的样式了。
注意:overrideUserInterfaceStyle 默认值为 unspecified ,所以一定要用 traitCollection.userInterfaceStyle 来判断当前样式,而不是用 overrideUserInterfaceStyle 来判断。

 

 

注意:以上代码是我自己摸索出来的,在真机上也能达到效果,但是不建议现在就开始做 DarkMode 的适配。毕竟官方关于 DarkMode 适配的 session 还没出,建议等 session 出了之后在做适配,另外如果有和官方有出入我会及时补充修改~

 

2019 iOS马甲包过审经验4.3和2.1的过包技巧

1. 机审原理
我们虽然无法得知苹果实际的机审原理,但从程序员的角度还是能分析出一些东西的。

1.1 首先OC和C++代码编译出的二进制文件,有点经验和反编译过的应该都知道:
删注释神马的是没用的,因为注释是不会被编译进包里
改类名是靠谱的,因为反编译出来能看到类名,改掉它显然是会造成包不一样
增改函数也是靠谱的,同样是因为反编译能看到
改文件夹或者文件名应该是不太靠谱的,编译的时候会根据路径来引用查找,编译之后应该是根据在包里的相对内存地址来查找类和函数,跟你编译时的文件名称和路径关系应该不大。不过为了方便和代码的统一,更换时可以顺便换了。
1.2 然后是一些资源文件如图片、音效
路径和文件名相当可能或者*对是有用的,可惜修改代价有点高
文件的md5值以程序员的角度来看才是真正区分文件是否一致的标准,我们有理由相信我们的苹果同行也用了这个来判断是否重复。所以一些修改md5值的操作如添加空行、注释、额外字节,应该也被考虑加上。
1.3 *后是相似的判定,应该是相似率高于某个值才认为你跟其他的重复了,针对像改资源文件名这种代价太高的可能暂不考虑的操作,我们只能添加垃圾文件提高总值来降低重复率了。
2. 混淆方法
2.1 修改类名文件名
先说下手动操作,无非是在xcode上修改文件名类名,然后在可能引用的地方替换类名和文件名(header),要注意的地方是替换的时候要选中匹配大小写;然后是文件夹名称跟文件名一样的时候,可能文件夹名称也要跟着改名,否则替换之后路径引用可能找不到。 招ios app马甲包套壳上架技术(个人、团队)H5接口、*光推送、关键词、介绍图、标题。
如果是要脚本批量操作,那*好先对工程整理下,确保以下几点能让脚本写的更简单更可靠:

要修改的类和文件*好都放到一个文件夹下,万一搞出事不用东找西找,备份和回滚也简单一点
类名和文件名尽量带上前缀,这样修改只替换前缀即可,也不太会跟函数名、变量名什么的重复
*好过一遍把不能修改类名的列出来,比如外面太多地方调用的、第三方的类库。在写脚本的时候把他们排除在外
脚本的话就是遍历文件,判断前缀、是否排除在外,修改文件名类名,在其他文件中查找替换。用python的话应该不是什么大问题。一个小技巧是改完后可以替换一下xxx.xcodeproj/project.pbxproj里的相应字符串,这样xcode打开工程的时候就不用手动再添加进来。

2.2 添加垃圾函数
OC头文件的声明必然是在@interface @end之间,实现是在@implementation @end之间,C++的大部分应该是以}结尾,直接在相应的地方插入垃圾函数,模板可以直接写个HelloWorld输出个随机字符串。在这一步随机名称是个坑,可以去网上找下常见英文单词,格式化后把太短的、太长的、看着不爽的,*重要的是语言的关键字如break,false,if之类的删掉

2.3添加垃圾类
根据我们猜测的路径应该是不影响打包的,所以我们可以简单的把垃圾类文件都放到同一个文件夹下方便管理,写好2.2后这一步应该就是顺手的事情了 。我不太确定的是如果外部不引用这些垃圾类,编译之后它们会不会因为太独立而被检测认为是垃圾代码。所以保险起见,我实现的时候写了一个单独的头文件include了所有这些生成的垃圾类,然后在外部include了这个单独的头文件

2.4修改资源md5值
资源文件有很多类型,通常来说文本文件添加随机数量的空格或空行应该就可以了。图片的话常见的png和jpg都是有固定的结尾字节块的,png是00 00 00 00 49 45 4E 44 AE 42 60 82,jpg是ffd9,用16进制查看工具打开图片应该能注意到这个规律,也可以参考下常见图片文件格式简析。在结尾字节块添加的内容是不会影响图片本身显示的,我们可以利用这个来改变图片的md5值。音效应该也有相应的格式,期待大佬科普下!

2.5创建资源垃圾文件
跟2.3类似,不过这个*好也随机下创建文件夹显得真实点,一些文本文件是什么格式都有各自定义,png和jpg的话就随机写任意长度的任意字符,*好结尾加上相应的结尾字节块,防止2.5后又执行2.4导致出错。

3. 其他事项
上面的基本都能脚本自动化执行,完了后工程名*好也在xcode改下;info.plist会被打包进ipa,*好也多加几个字段上去;target能改也改下方便识别;scheme关联到导出的ipa文件名,不是特别麻烦也顺手改掉;包名、启动页、图标应该都是基本的东西不会被忽略。
———————
一、只改了APP图标和bundleId
Guideline 4.3 – Design
This app duplicates the content and functionality of other apps submitted by you or another developer to the App Store, which is considered a form of spam.

显然已经被标定为重复APP了,机器审核应该就已经发现相似度很高了,然后当晚我打开公司APP监控,在审核这个甲方APP期间,公司的APP被打开了,显然机器审完后,人工还做了一次校验,发现两个APP几乎一样,囧。

二、加入垃圾代码和更改类名
这里我主要做的是,找一些平时练习的工程或者测试工程,把能用的全部拉拉进去,管他是啥,编译不报错就行了。
因为本身学了一些Python的基础,然后我参考网上的一些教程写了一个用Python一键更改类名前缀和后缀的脚本,这样类名也变了,我想应该差差不多了吧。
因为之前4.3了,被警告并延迟审核了,为了快速审核,我移除了那个APPID,重建一个id,这样第二天就得到审核了。然而…

Guideline 4.3 – Design …

what?! 还是一样的结果

三、新建工程,并更改资源文件MD5
这里我想到了,我原来的Project都跟之前的一样,所有配置参数都一样,这样可能比较容易被发现。于是我新建了一个项目,工程名称也用新的,然后调一调工程基础设置,还是用第二点的方式,进行处理。
同时我查到资源文件MD5也可能被苹果的机器审核进行了记录。于是想办法在不改变图片的情况下,更改文件的MD5值,于是了解到文件的二进制原理,于是做了下尝试在图片的流数据末尾混入垃圾数据,结果真的可以在不改变图片展示的情况下,成功修改了图片的MD5,同样在Python一键更改类名前缀和后缀的脚本有脚本代码,可自行更改。该做的都做了,于是又新建了个APPID重新提交。

Guideline 4.3 – Design …

我去?! 怎么还过不了

四、更改首页(假页面)、在其他IP地址下打包上传APP
查了比较多资料,看看我的工程,该做的都做了,机器审核应该发现不了了吧,莫非是我打包APP的ip地址苹果也会记录,既然如此那就,用我的手机给电脑发热点,然后打包吧。
同时,既然过了机器,那怎么过人工呢,人工肯定是肉眼来看的,那就用假页面骗骗他吧。本来我们的APP是开放的进首页点击的时候再登录的,我在后台做了个接口配置,让他在审核的时候必须先登录才能进首页,进入的首页,根据给他的账号,跳转到假页面上…差不多就这样

Guideline 2.1 – Information Needed…
This type of app has been identified as one that may violate one or more of the following App Store Review Guidelines. Specifically, these types of apps often:
1.1.6 – Include false information, features, or misleading metadata.
2.3.1 – Have hidden or undocumented features, including hidden “switches” that redirect to a gambling or lottery website

等等!这不是2.1大*包吗?然而身经百战的我根本不慌,我直接回复:“我们确认,我们的APP不存在你说的任何问题”,也可以参考网上的一些2.1大*包的回复格式。

*终,苹果审核人员第二天就妥协了,运气还不错!
仅供参考,毕竟每个APP应用场景不同

iOS–再也不用担心数组越界

*近在网易云捕上看到一些数组越界导致的崩溃日志,所以决定数组的越界做一些处理。

崩溃报错信息

在项目的开发中,笔者一般遇到的问题就是,数组越界:

-[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array;
-[__NSArrayM objectAtIndexedSubscript:]: index 0 beyond bounds for empty array;
很明显,这两个函数是在数组取值的时候发生的越界情况,在网上搜索了很多大神的文章,也自己总结了一下,下面罗列出两种处理方法:

一、为NSArray、NSMutableArray添加分类并添加方法

首先,我们为NSarray创建分类并添加方法,在.h文件中:

@interface NSArray (ErrorHandle)

/**
为数组分类添加的方法  可以在应用中直接调用 可以防止数组越界导致的crash
@param index 传入的取值下标
@return id类型的数据
*/
– (id)objectAtIndexVerify:(NSUInteger)index;
– (id)objectAtIndexedSubscriptVerify:(NSUInteger)idx;
@end
在.m文件中,我们可以将这两个方法实现出来:

@implementation NSArray (ErrorHandle)
/**
*  防止数组越界
*/
– (id)objectAtIndexVerify:(NSUInteger)index{
if (index < self.count) {
return [self objectAtIndex:index];
}else{
return nil;
}
}
/**
*  防止数组越界
*/
– (id)objectAtIndexedSubscriptVerify:(NSUInteger)idx{
if (idx < self.count) {
return [self objectAtIndexedSubscript:idx];
}else{
return nil;
}
}
类似的,我们可以为NSMutableArray创建分类,(在可变数组中,我们插入nil对象也会产生crash,所以我们要对可变数组做特殊处理)

#import

@interface NSMutableArray (ErrorHandle)
/**
数组中插入数据
@param object 数据
@param index 下标
*/
– (void)insertObjectVerify:(id)object atIndex:(NSInteger)index;
/**
数组中添加数据
@param object 数据
*/
– (void)addObjectVerify:(id)object;

@end
在可变数组的.m文件中

@implementation NSMutableArray (ErrorHandle)
/**
*  数组中插入数据
*/
– (void)insertObjectVerify:(id)object atIndex:(NSInteger)index{
if (index < self.count && object) {
[self insertObject:object atIndex:index];
}
}
/**
*  数组中添加数据
*/
– (void)addObjectVerify:(id)object{
if (object) {
[self addObject:object];
}
}
特别说明:以上方法在项目的实际运用中,要想防止数组越界,就需要调用我们自己添加的方法了,而不要调用系统的了。

二、用runtime处理数组越界

不到万不得已,笔者一般是不想用runtime的。不过runtime确确实实也能解决数组越界的问题,在我们数组越界处理的*种方法中,我们可以看见,我们无法使用索引来从数组中取值了(即类似:cell.textLabel.text = self.dataSource[indexPath.row];这样的取值方式)。那如果我们想要这种取值方式的话,就需要用runtime来实现了。

首先,我们先来确定下self.dataSource[indexPath.row]这样的取值到底调用了何种方法:

通过报错的函数,我们可以发现,数组索引调用的是objectAtIndexedSubscript:这个函数。找到了报错的函数,我们就可以通过runtime来实现函数的交换。首先,我们为NSObject写一个分类,方便我们调用交换系统和自定义的方法:

#import

@interface NSObject (SwizzleMethod)

/**
*  对系统方法进行替换(交换实例方法)
*
*  @param systemSelector 被替换的方法
*  @param swizzledSelector 实际使用的方法
*  @param error            替换过程中出现的错误消息
*
*  @return 是否替换成功
*/
+ (BOOL)SystemSelector:(SEL)systemSelector swizzledSelector:(SEL)swizzledSelector error:(NSError *)error;
@end
在.m文件中,我们需要将方法实现出来:

#import “NSObject+SwizzleMethod.h”
#import

@implementation NSObject (SwizzleMethod)

/**
*  对系统方法进行替换
*
*  @param systemSelector 被替换的方法
*  @param swizzledSelector 实际使用的方法
*  @param error            替换过程中出现的错误消息
*
*  @return 是否替换成功
*/
+ (BOOL)SystemSelector:(SEL)systemSelector swizzledSelector:(SEL)swizzledSelector error:(NSError *)error{

Method systemMethod = class_getInstanceMethod(self, systemSelector);
if (!systemMethod) {
return NO;
}

Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
if (!swizzledMethod) {

return NO;
}

if (class_addMethod([self class], systemSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {

class_replaceMethod([self class], swizzledSelector, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
method_exchangeImplementations(systemMethod, swizzledMethod);
}

return YES;
}
@end
在方法交换和替换的过程中,如果被替换的方法或者我们将要使用的方法没有的话,直接ruturn,不进行方法互换,经过双重检验才进行方法的互换。

我们以NSMutableArray为例子,同样的NSMutableArray添加分类,在.h文件中只需要写下如下代码:

+(void)load{
[super load];
//无论怎样 都要保证方法只交换一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//交换NSMutableArray中的方法
[objc_getClass(“__NSArrayM”) SystemSelector:@selector(objectAtIndex:) swizzledSelector:@selector(jz_objectAtIndex:) error:nil];
//交换NSMutableArray中的方法
[objc_getClass(“__NSArrayM”) SystemSelector:@selector(objectAtIndexedSubscript:) swizzledSelector:@selector(jz_objectAtIndexedSubscript:) error:nil];
});
}

– (id)jz_objectAtIndex:(NSUInteger)index{
if (index < self.count) {
return [self jz_objectAtIndex:index];
}else{

NSLog(@” 你的NSMutableArray数组已经越界 帮你处理好了%ld   %ld   %@”, index, self.count, [self class]);
return nil;
}
}
– (id)jz_objectAtIndexedSubscript:(NSUInteger)index{
if (index < self.count) {

return [self jz_objectAtIndexedSubscript:index];
}else{
NSLog(@” 你的NSMutableArray数组已经越界 帮你处理好了%ld   %ld   %@”, index, self.count, [self class]);
return nil;
}
}
同样的,我们也可以在NSArray的分类中添加如下代码:

+(void)load{
[super load];
//无论怎样 都要保证方法只交换一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//交换NSArray中的objectAtIndex方法
[objc_getClass(“__NSArrayI”) SystemSelector:@selector(objectAtIndex:) swizzledSelector:@selector(sxy_objectAtIndex:) error:nil];
//交换NSArray中的objectAtIndexedSubscript方法
[objc_getClass(“__NSArrayI”) SystemSelector:@selector(objectAtIndexedSubscript:) swizzledSelector:@selector(sxy_objectAtIndexedSubscript:) error:nil];
});
}

– (id)sxy_objectAtIndexedSubscript:(NSUInteger)idx{
if (idx < self.count) {
return [self sxy_objectAtIndexedSubscript:idx];
}else{
NSLog(@” 你的 NSArray数组已经越界了 但是已经帮你处理好了  %ld   %ld”, idx, self.count);
return nil;
}
}

– (id)sxy_objectAtIndex:(NSUInteger)index{
if (index < self.count) {
return [self sxy_objectAtIndex:index];
}else{
NSLog(@” 你的 NSArray数组已经越界了 但是已经帮你处理好了  %ld   %ld”, index, self.count);

return nil;
}
}
关于上面的Demo,笔者已经上传git,需要的小伙伴去下载吧!数组越界Demo

总结:以上两种方法目前用的都可行,貌似用runtime封装虽然复杂一点,但是使用起来更为隐蔽,也更自如一些,并且之前的数组取值不用做改动。大家在项目中两种方法,可以喜欢哪种用哪种了,妈妈再也不用担心我的数组越界了!!!(此处只是添加了数组取值时候的防止越界,在实际项目中可能用到的不止这几种方法(例如插入),大家可以根据自己的实际需要添加)

iOS删除列表某行数据时出现: reason: -[__NSArrayM objectAtIndexedSubscript:]: index 4 beyond bounds [0 .. 3]’

错误原因:

数组中有5个元素,列表有5个数据,在删除列表数据并刷新列表后,数组取值的时候没有进行判断。

解决方法:

在取值的时候,需要判断数组元素大于列表数据或者列表数据小于数组元素

if(self.dataSources.count>indexPath.row)或者 if(indexPath.row<self.dataSources.count)

189. 旋转数组(JS实现)

189. 旋转数组(JS实现)

1 题目
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [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]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。

链接:https://leetcode-cn.com/problems/rotate-array

2 思路
这道题将数组元素向右移动k个位置,就是每个元素移动后的索引为i+k,因此,我们可以将每个元素都向右移动k个位置,即得到答案

3代码
/**
* @param {number[]} nums
* @param {number} k
* @return {void} Do not return anything, modify nums in-place instead.
*/
var rotate = function(nums, k) {
let len = nums.length;
k = k % len;
if (k === 0) return;

let count = 0;

for (let start=0; count < len; start++) {
let current = start;
let next = start + k;
let prev = nums[current]; //记录当前元素值

do {
let temp = nums[next]; //记录下个元素值
nums[next] = prev; //替换元素
prev = temp;
current = next;
next += k;
next %= len; //索引超出时回到数组开头
count++; //记录已经移动的元素的个数
} while(current !== start)
}
};

颠倒二进制位(JS实现)

颠倒二进制位(JS实现)

1 题目
颠倒给定的 32 位无符号整数的二进制位。
示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
示例 2:
输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 2 中,输入表示有符号整数 -3,输出表示有符号整数 -1073741825。
进阶:
如果多次调用这个函数,你将如何优化你的算法?

链接:https://leetcode-cn.com/problems/reverse-bits

2 思路
这道题将n和1做与&运算,即可取到n*末尾的1或0,然后将其加到结果上,*后的res >>> 0是将其表示为无符号整数的形式,否则可能是负数

3代码
/**
* @param {number} n – a positive integer
* @return {number} – a positive integer
*/
var reverseBits = function(n) {
let res = 0;

for (let i=0; i<32; i++) {
res = (res << 1) + (n & 1);
n = n >> 1;
}

return res >>> 0;
};

打家劫舍(JS实现)

打家劫舍(JS实现)

1 题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的*高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的*高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的*高金额 = 2 + 9 + 1 = 12 。

链接:https://leetcode-cn.com/problems/house-robber

2 思路
这道题思路就是动态规划,状态转移方程 d[i] = Math.max(d[i-2] + nums[i], d[i-1]), d[i]为第i个房屋时*大利润

3代码
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if (nums.length === 0) return 0;
if (nums.length === 1) return nums[0];

let d = [nums[0]];

for (let i=1; i<nums.length; i++) {
d[i] = Math.max(d[i-1], i – 2 >= 0 ? d[i-2] + nums[i] : nums[i]);
}

return d[nums.length – 1];
};

二叉树的右视图(JS实现)

二叉树的右视图(JS实现)

1 题目
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <—
/
2 3 <—
\
5 4 <—

链接:https://leetcode-cn.com/problems/binary-tree-right-side-view

2 思路
这道题我们可以从对树进行中序遍历,但得先遍历树的右分支,再左分支,在遍历的过程中,记录每个节点的当前深度和当前遍历过程的*大深度,若当前节点的深度大于当前*大深度,说明该节点从右侧可以看见,则将其加入结果中

3代码
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var rightSideView = function(root) {
let nums = [];
if (!root) return nums;

let stack = [];
let p = root;
let maxDepth = 0;
let currentDepth = 0;
while(p || stack.length > 0) {
while(p) { //遍历节点的右分支
currentDepth++;
if (currentDepth > maxDepth) { //推入节点
maxDepth++;
nums.push(p.val);
}
stack.push([p, currentDepth]);
p = p.right;
}

let node = stack.pop(); //回溯
p = node[0].left; //对节点的左分支进行遍历
currentDepth = node[1]; //当前深度也要回溯
}

return nums;
};

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速