1.逆波兰算法
题目:
求["2", "1", "+", "3", "*"];
前置知识:
(1)在求解逆波兰算法前,需要先了解四则运算的表达式,四则运算的表达式分3中:前缀表达式(又称波兰表达式)、中缀表达式、后缀表达(又称逆波兰表达式);
以上三种表达式是等价的。
(2)栈:栈是先入后出的,所以,这里可以借用栈的思路来解题。
解题思路:
(1)遍历数组;
(2)遇到数字,直接入栈;
(3)遇到符号:
- 弹出栈顶的右操作数
- 弹出栈顶的左操作数
- 使用符号进行计算,将计算的结果放入栈中
(4)最后栈中的唯一数字就是后缀表达式计算的值。
代码实现:
因为OC没有用直接可用的栈代码,有两种方式来手动模拟栈的操作:
(1)用代码实现一个栈的功能,可参考如下链接:https://www.jianshu.com/p/0ae0310e0263;
(2)用数组模拟栈的操作。
目前只是用数组来模拟栈的操作,代码如下:
- (void)reverseTest:(NSArray *)array {
//逆波兰,算出下列的值: [2 1 + 3 *]
NSMutableArray *stackArray = [NSMutableArray array];
for (NSString *object in array) {
if ([self isOperator:object]) {
//取出栈顶的值 操作
NSString *right = stackArray.lastObject;
[stackArray removeLastObject];
NSString *left = stackArray.lastObject;
[stackArray removeLastObject];
NSInteger caclulate = [self caclulateWithLeft:[left intValue] right:[right integerValue] opertator:object];
[stackArray addObject:[NSString stringWithFormat:@"%ld", caclulate]];
}else {
//如果是数字,就存入栈中
[stackArray addObject:object];
}
}
NSLog(@"%@", stackArray.lastObject);
}
- (BOOL)isOperator:(NSString *)object {
return [@"+-*/" containsString:object];
}
- (NSInteger)caclulateWithLeft:(NSInteger)left right:(NSInteger)right opertator:(NSString *)operator{
if ([operator isEqualToString:@"+"]) {
return left + right;
}
if ([operator isEqualToString:@"-"]) {
return left - right;
}
if ([operator isEqualToString:@"*"]) {
return left * right;
}
if ([operator isEqualToString:@"/"]) {
return left / right;
}
return 0;
}
2.合并两个有序列表
题目:
前置知识:
链表(Linked List)是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,表现形式如下图所示:
iOS没有现成的链表结构,如果需要的话,需要自己代码实现。
解题思路:
这个思路不太好说,一切尽在代码中吧
代码实现:
新建一个链表:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LYZLinkNode : NSObject
@property (nonatomic, copy) NSString * key;
@property (nonatomic, strong) LYZLinkNode * next ;
@end
实现:
- (LYZLinkNode *)merginLinkerWithNode1:(LYZLinkNode *)node1 node2:(LYZLinkNode *)node2 {
if (node1 == nil) return node2;
if (node2 == nil) return node1;
LYZLinkNode *head = [[LYZLinkNode alloc] init];
if ([node1.key intValue] < [node2.key intValue]) {
head = node1;
node1 = node1.next;
}else {
head = node2;
node2 = node2.next;
}
LYZLinkNode *currentNode = head;
while (node1 != nil && node2 != nil) {
if ([node1.key intValue] <= [node2.key intValue]) {
currentNode = currentNode.next = node1;
node1 = node1.next;
}else {
currentNode = currentNode.next = node2;
node2 = node2.next;
}
}
if (node1 == nil) {
currentNode = currentNode.next = node2;
}
if (node2 == nil) {
currentNode = currentNode.next = node1;
}
return head;
}
3.合并K个有序链表
这个是在上一个算法题的前提下继续研究的。解题思路有多重,目前,我只实现了三种:
第一种:两两合并
/** 合并k个有序链表---两两合并 */
- (LYZLinkNode *)merginKLinkedWithArray:(NSArray *)array {
NSMutableArray *nodeArray = [NSMutableArray arrayWithArray:array];
for (NSInteger i = 1; i < nodeArray.count; i++) {
LYZLinkNode *node = [self merginLinkerWithNode1:nodeArray[0] node2:nodeArray[i]];
[nodeArray replaceObjectAtIndex:0 withObject:node];
}
return nodeArray[0];
}
第二种:最小堆顶
思路:把链表加入数组中,在数组中把最小值弹出,并把最小值的next值加入数组中
/** 合并k个有序链表---最小堆顶 */
- (LYZLinkNode *)merginKLinkedWithMiniStackTop:(NSArray *)array {
LYZLinkNode *node = [[LYZLinkNode alloc] init];
LYZLinkNode *current = node;
NSMutableArray *arrayM = [NSMutableArray arrayWithArray:array];
int first = -1;
while (true) {
arrayM = [[arrayM sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [self compare:obj1 node2:obj2];
}] mutableCopy] ;
LYZLinkNode *n1 = arrayM[0];
if (first == -1) {
current = node = n1;
first = 1;
[arrayM replaceObjectAtIndex:0 withObject:current.next];
}else {
if (n1.next == nil) {
current = current.next = n1 ;
[arrayM removeObjectAtIndex:0];
}else {
current = current.next = n1 ;
[arrayM replaceObjectAtIndex:0 withObject:n1.next];
}
}
if (arrayM.count == 0) {
break;
}
}
return node;
}
- (NSComparisonResult)compare:(LYZLinkNode *)node1 node2:(LYZLinkNode *)node2 {
NSInteger key1 = [node1.key integerValue];
NSInteger key2 = [node2.key integerValue];
if (key1 == key2) {
return NSOrderedSame;
}
if (key1 > key2) {
return NSOrderedDescending;
}
return NSOrderedAscending;
}
第三种:分治策略
如上图可见:把两个链表合并并赋值给第一个链表
/** 合并k个有序链表--分治策略 */
- (LYZLinkNode *)merginKLinkenWithFenZhiCeLue:(NSArray *)array {
if (array.count == 0) {
return nil;
}
int step = 1;
while (step < array.count) {
for (NSInteger i = 0; i < array.count; i += 2*step) {
if (i+1 >= array.count) {
break;
}
[self merginLinkerWithNode1:array[i] node2:array[i+step]] ;
}
step *= 2;
}
return array[0];
}
4.验证二叉搜索树
题目:
给定一个二叉树,判断其是否是有效的二叉搜索树
前置知识:
先序遍历:先访问根节点,再先序遍历左子树,最后先序遍历右子树;
中序遍历:先中序遍历左子树,再访问根节点,最后中序遍历右子树;
后序遍历:先后序遍历左子树,再后序遍历右子树,最后访问根节点。
解题思路:
递归思路
代码实现:
方法一:
方法二:
5.二叉树中的最大路径和
题目:
实例:
解题思路:
代码实现:
创建一个二叉树的类:
------.h-----
@interface LYZTreeNode : NSObject
@property (nonatomic, assign) int value ;
@property (nonatomic, strong) LYZTreeNode * leftNode ;
@property (nonatomic, strong) LYZTreeNode * rightNode ;
- (void)treeNodeWithValue:(int)value left:(LYZTreeNode *)left right:(LYZTreeNode *)right;
@end
------.m-------
@implementation LYZTreeNode
- (void)treeNodeWithValue:(int)value left:(LYZTreeNode *)left right:(LYZTreeNode *)right {
self.value = value;
self.leftNode = left;
self.rightNode = right;
}
@end
/----------实现--------/
int sum = 0;
- (int)treeNodeValue:(LYZTreeNode *)node {
if (node == nil) {
return 0;
}
int leftValue = MAX([self treeNodeValue:node.leftNode], 0);
int rightValue = MAX([self treeNodeValue:node.rightNode], 0);
sum = MAX(leftValue + node.value + rightValue, sum);
return MAX(MAX(leftValue, rightValue) + node.value, 0) ;
}
6.二叉树展开为链表
题目:
解题思路:
方法一:先序遍历
(1)把右节点用单独的变量A存储;
(2)把左节点赋值给二叉树的右节点;把左节点清空
(3)把右节点变量A赋值给当前二叉树的最后遍历到的右节点
(4)遍历右节点,重复上面的步骤。
方法二:后续遍历
(1)遍历二叉树的右节点;
(2)编辑二叉树的左节点;
(3)把上一次遍历到的节点赋值给二叉树的右节点
(4)清空左节点;
代码实现:
方法一:
- (void)qianXuPreView:(LYZTreeNode *)node {
if (node == nil) {
return;
}
LYZTreeNode *oldRightNode = node.rightNode;
node.rightNode = node.leftNode;
node.leftNode = nil;
LYZTreeNode *rootNode = node;
while (rootNode.rightNode != nil) {
rootNode = rootNode.rightNode;
}
rootNode.rightNode = oldRightNode;
[self qianXuPreView:node.rightNode];
}
方法二:
- (void)houXuPreView:(LYZTreeNode *)node {
if (node == nil) {
return;
}
[self houXuPreView:node.rightNode];
[self houXuPreView:node.leftNode];
if (self.preView != nil) {
node.rightNode = self.preView;
node.leftNode = nil;
}
self.preView = node;
}
7. 打家劫舍
题目:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的
房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
解题思路:
方法一:递归的思想
[1, 2, 3, 4, 5, 6]
第一步:
如果第一家打劫的话,只能从[3, 4, 5, 6]家中打劫出最多的钱
如果第一家不打劫的话,就从[2, 3, 4, 5, 6]家中打劫出最大的钱
第二步:
如果打劫第一家了,那么[3, 4, 5, 6]最大的打劫金额 按照第一步的思路来计算
如果打劫了3这一家,那么就从[5, 6]家中打劫出最多的钱
如果不打劫3这一家,那么就从[4, 5, 6]家中打劫出最多的钱
如果不打劫第一家,那么[2, 3, 4, 5, 6]最大的打劫金额 也按照第一步的思路来计算
如果打劫2这一家,那么就从[4, 5, 6]中打劫出最多的钱
如果不打劫2这一家,那么就从[3, 4, 5, 6]中打劫出最多的钱
第三步:
如果打劫了3这一家,那么[5, 6]家中打劫最大的钱就是max(5,6)
如果不打劫3这一家,那么[4, 5, 6]家打劫出最大的钱,按照第一步的思路计算....
方法二:非递归思想
自定义一个数组,这个数组存放的是每一家能打劫出来的最多的钱的值
每一家能打劫出来的值就是 max( 自己这一家的钱 + 上上一家可能打劫出来最大的钱, 上一家能打劫出来的最大的值)
代码实现:
方法一:
- (int)rob:(NSArray *)array begin:(int)begin {
if (begin == array.count - 1) {
return [array[begin] intValue];
}
if (begin == array.count - 2) {
return MAX([array[begin] intValue], [array[begin + 1] intValue]) ;
}
int robFirst = [array[begin] intValue] + [self rob:array begin:begin + 2];
int robSecond = [self rob:array begin:begin + 1];
return MAX(robFirst, robSecond);
}
-----调用-----
NSArray *array = @[@"1", @"3", @"5", @"8", @"4", @"11"];
NSLog(@"rob = %d", [self robWithoutArray:array]);
方法二:
- (int)rob:(NSArray *)array {
if (array.count == 1) {
return [array[0] intValue];
}
NSMutableArray *valueArray = [NSMutableArray array];
valueArray[0] = array[0];
valueArray[1] = array[1];
for (int i = 2; i < array.count; i++) {
NSInteger maxValue = MAX([array[i] integerValue] + [valueArray[i - 2] integerValue], [valueArray[i - 1] integerValue]);
valueArray[i] = [NSString stringWithFormat:@"%ld", maxValue];
}
return [valueArray[array.count - 1] intValue];
}
优化第二种的方法:
- (int)robWithoutArray:(NSArray *)array {
if (array.count == 1) {
return [array[0] intValue];
}
int firstValue = [array[0] intValue];
int secondValue = [array[1] intValue];
int tem ;
for (int i = 2; i < array.count; i++) {
tem = MAX([array[i] intValue] + firstValue, secondValue);
firstValue = secondValue;
secondValue = tem;
}
return secondValue;
}
8.取前K个高频数
题目:
思路:
1.遍历数组,以出现的值为key,出现的次数为Value,保存到一个字典中;
2.利用散列表的思路 将字典存放的数据 转成 数组存放,每个key值出现的次数,当做数组的索引,将数据存放进去。
代码实现:
//遍历数组
- (NSMutableDictionary *)dictinaryOrderByArray:(NSArray *)array {
NSMutableDictionary *dicM = [NSMutableDictionary dictionary];
for (NSString *numberS in array) {
NSNumber *number = dicM[numberS];
if (number == nil) {
number = @0;
}
int value = number.intValue ;
value++;
[dicM setValue:[NSNumber numberWithInt:value] forKey:numberS];
}
return dicM;
}
- (void)getKOffenValue:(NSDictionary *)dic k:(int)k {
//先创建跟字典个数相同的空数组集合
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:dic.count];
for (int i = 0; i < dic.count; i++) {
[arrayM addObject:[NSMutableArray array]];
}
//把key值对应的出现的次数放到那个次数为索引的数组中(类似于散列表),因为要考虑到可能有的出现次数会相同,所以最好用数组来存放
NSArray *keys = dic.allKeys;
for (NSString *keyString in keys) {
NSNumber *number = dic[keyString];
NSMutableArray *arrayInt = arrayM[number.integerValue];
[arrayInt addObject:keyString];
[arrayM replaceObjectAtIndex:number.intValue withObject:arrayInt];
}
//取出前k个高频数字
NSMutableArray *arrayK = [NSMutableArray array];
for (int i = arrayM.count - 1; i > 0 && arrayK.count < k; i--) {
NSArray *array = arrayM[i];
[arrayK addObjectsFromArray:array];
}
NSLog(@"array = %@", arrayK);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSArray *array = @[@"1", @"1", @"2", @"3", @"4", @"3", @"3"];
[self getKOffenValue:[self dictinaryOrderByArray:array] k:2];
}