获取两个View最近的共同父View和所有的共同父View
// 获取一个view的所有父view,将他们放到set里面
- (NSSet*)parentViewsForView:(UIView*)view {
NSMutableSet *parentViewsSet = [NSMutableSet set];
UIView *parentView = view;
while (parentView != nil) {
[parentViewsSet addObject:parentView];
parentView = parentView.superview;
}
return [NSSet setWithSet:parentViewsSet];
}
// 最近的共同父View
- (UIView*)nearestParentViewForView1:(UIView*)view1 view2:(UIView*)view2 {
NSSet *view1ParentsSet = [self parentViewsForView:view1];
UIView *parentView = view2;
while (parentView != nil) {
if ([view1ParentsSet containsObject:parentView]) {
return parentView;
}
else {
parentView = parentView.superview;
}
}
return nil;
}
// 所有的共同父View
- (NSSet*)commonParentsViewForView1:(UIView*)view1 view2:(UIView*)view2 {
NSMutableSet *view1ParentsSet = [NSMutableSet setWithSet:[self parentViewsForView:view1]];
[view1ParentsSet intersectSet:[self parentViewsForView:view2]];
return [NSSet setWithSet:view1ParentsSet];
}
合并两个已排好序的数组
// 双指针
- (NSArray*)mergeSortedArray1:(NSMutableArray*)sortedArray1
sortedArray2:(NSMutableArray*)sortedArray2 {
int i = 0;
int j = 0;
NSMutableArray *mergedArray = [NSMutableArray arrayWithCapacity:sortedArray1.count+sortedArray2.count];
while (i < sortedArray1.count && j < sortedArray2.count) {
if ([sortedArray1[i] integerValue] < [sortedArray2[j] integerValue]) {
mergedArray[i+j] = sortedArray1[i];
i += 1;
}
else {
mergedArray[i+j] = sortedArray2[j];
j += 1;
}
}
// 这里其实可以修改成 if语句,添加数组的剩余数据
while (i < sortedArray1.count) {
mergedArray[i+j]= sortedArray1[i];
i += 1;
}
while (j < sortedArray2.count) {
mergedArray[i+j]= sortedArray2[j];
j += 1;
}
return [NSArray arrayWithArray:mergedArray];
}
已知单向链表,其中每个节点有一个int类型的data字段(0~9)和一个next指针,按照如下的方式连接3->5->2表示352,请写出两个链表做加法
// 定义链表节点
@interface Node : NSObject
@property (nonatomic, assign) int data;
@property (nonatomic, strong) Node *next;
@end
@implementation Node
- (NSString *)description {
NSMutableString *str = [NSMutableString string];
Node *node = self;
while (node) {
[str appendFormat:@"%d",node.data];
node = node.next;
}
return [str copy];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 构建链表1
Node *node1 = [[Node alloc] init];
node1.data = 3;
Node *tmp = node1;
NSArray *nums = @[@(7),@(8),@(2)];
for (NSNumber *num in nums) {
Node *nextNode = [[Node alloc] init];
nextNode.data = [num intValue];
tmp.next = nextNode;
tmp = nextNode;
}
// 构建链表2
Node *node2 = [[Node alloc] init];
node2.data = 0;
tmp = node2;
nums = @[@(6),@(1),@(5),@(8),@(0)];
for (NSNumber *num in nums) {
Node *nextNode = [[Node alloc] init];
nextNode.data = [num intValue];
tmp.next = nextNode;
tmp = nextNode;
}
// 调用链表加法算法
[self nodeAdd:node1 node2:node2];
}
- (Node*)nodeAdd:(Node*)node1 node2:(Node*)node2 {
// 3->5->1 要算加法,肯定要倒序链表
NSMutableArray *rNode1 = [NSMutableArray arrayWithCapacity:20];
Node *tmp = node1;
// 反转node1
while (tmp != nil) {
[rNode1 addObject:tmp];
tmp = tmp.next;
}
// 反转node2
NSMutableArray *rNode2 = [NSMutableArray arrayWithCapacity:20];
tmp = node2;
while (tmp != nil) {
[rNode2 addObject:tmp];
tmp = tmp.next;
}
NSEnumerator *rNode1E = [rNode1 reverseObjectEnumerator];
NSEnumerator *rNode2E = [rNode2 reverseObjectEnumerator];
// 进行计算
NSMutableArray *calculateNodes = [NSMutableArray arrayWithCapacity:MAX(rNode1.count, rNode2.count)];
Node *calculate1 = rNode1E.nextObject;
Node *calculate2 = rNode2E.nextObject;
// 进位标识
int flag = 0;
while (calculate1 != nil && calculate2 != nil) {
int num = calculate1.data + calculate2.data + flag;
if (num >= 10) {
flag = 1;
num -= 10;
}
else {
flag = 0;
}
Node *tmp = [[Node alloc] init];
tmp.data = num;
[calculateNodes addObject:tmp];
calculate1 = rNode1E.nextObject;
calculate2 = rNode2E.nextObject;
}
while (calculate1 != nil) {
int num = calculate1.data + flag;
if (num >= 10) {
flag = 1;
num -= 10;
}
else {
flag = 0;
}
Node *tmp = [[Node alloc] init];
tmp.data = num;
[calculateNodes addObject:tmp];
calculate1 = rNode1E.nextObject;
}
while (calculate2 != nil) {
int num = calculate2.data + flag;
if (num >= 10) {
flag = 1;
num -= 10;
}
else {
flag = 0;
}
Node *tmp = [[Node alloc] init];
tmp.data = num;
[calculateNodes addObject:tmp];
calculate2 = rNode2E.nextObject;
}
//反转所得结果
NSEnumerator *node3E = [calculateNodes reverseObjectEnumerator];
tmp = node3E.nextObject;
Node *node3 = nil;
while (tmp != nil) {
if (node3 == nil && tmp.data > 0) {
node3 = tmp;
}
Node *m = node3E.nextObject;
tmp.next = m;
tmp = m;
}
#if DEBUG
NSLog(@"\n%@\n+\n%@\n=\n%@",node1,node2,node3);
#endif
return node3;
}
写一个算法获取十进制数进行二进制表示后1的个数
- (NSUInteger)countOfBinary1In:(NSInteger)num {
//(最优)采用位运算(x&(x-1),每次将给定的数的最右边的1变位0)
// 采用给定的数与给定的数-1逻辑与操作,把给定的数的最后边的1变成0,操作一次,count自增一次,知道给定的数的1全部变为0,退出循环
NSUInteger count = 0;
NSInteger mNum = num;
while (mNum != 0) {
mNum = mNum & (mNum - 1);
count++;
}
return count;
}
寻找一个字符串中不包含重复字符的最长子串的长度
// 滑动窗口方法
- (void)maxNoRepeatStrLengthFor:(NSString*)str {
NSMutableSet * set = [[NSMutableSet alloc] init];
int ans = 0;// 两个临近的相同字符的间距
int i = 0; //前序走的index
int j = 0; // 后序走的index
NSUInteger length = str.length;
while (j < length) {
NSString *subStrAtJ = [str substringWithRange:NSMakeRange(j,1)];
if (![set containsObject:subStrAtJ]) {
[set addObject:subStrAtJ];
j += 1;
// 判断两个相同字符的差距是不是和之前的更大
if (j - i > ans) {
ans = j - i;
}
}
else {
[set removeObject:[str substringWithRange:NSMakeRange(i, 1)]];
i += 1;
}
NSLog(@"111111111=== i %d j %d",i,j);
}
NSLog(@"%d", ans);
}
// 优化的滑动窗口
- (void)maxNoRepeatStrLengthFor1:(NSString*)str {
NSUInteger length = str.length;
// str : index
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:length];
int ans = 0;// 两个临近的相同字符的间距
int i = 0; //前序走的index
int j = 0; // 后序走的index
while (j < length) {
NSString *subStrAtJ = [str substringWithRange:NSMakeRange(j,1)];
if ([dict.allKeys containsObject:subStrAtJ]) {
//应该保证滑动窗口的起始位置依次向前,不能倒退
i = MAX(i, [dict[subStrAtJ] intValue] + 1);//max的作用 :考虑字符串abba这个例子
}
NSLog(@"2222222=== i %d j %d",i,j);
dict[subStrAtJ] = @(j);
j += 1;
ans = MAX(ans, j - i);
}
NSLog(@"%d", ans);
}
交换两个NSInteger的值你能想到几种方法
// 交换A和B的值
// 1.使用中间变量
- (void)swap0a:(NSInteger)a b:(NSInteger)b {
NSInteger temp = a;
a = b;
b = temp;
NSLog(@"%ld %ld",a,b);
}
// 2.加法
- (void)swap1a:(NSInteger)a b:(NSInteger)b {
a = a + b;
b = a - b;
a = a - b;
NSLog(@"%ld %ld",a,b);
}
// 3.异或(相同为0,不同为1. 可以理解为不进位加法)
- (void)swap2a:(NSInteger)a b:(NSInteger)b {
a = a ^ b;
b = a ^ b;
a = a ^ b;
NSLog(@"%ld %ld",a,b);
}
求两个NSInteger的最大公约数,这里假定a>b
/** 1.直接遍历法 */
- (NSInteger)maxCommonDivisorFor0A:(NSInteger)a b:(NSInteger)b {
NSInteger max = 0;
for (NSInteger i = 1; i <= b; i++) {
if (a % i == 0 && b % i == 0) {
max = i;
}
}
NSLog(@"%ld",max);
return max;
}
/** 2.辗转相除法 其中a为大数,b为小数 */
- (NSInteger)maxCommonDivisorFor1A:(NSInteger)a b:(NSInteger)b {
NSInteger r = a % b;
while(r > 0) {
a = b;
b = r;
r = a % b;
}
NSLog(@"%ld",b);
return b;
}
判断一个数是否为质数(只能被1和自身整除的叫质数)
- (BOOL)isPrime:(NSInteger)n {
for(int i = 2; i <= sqrt(n); i++) {
if(n % i == 0) {
return NO;
}
}
return YES;
}
给定一个字符串,输出本字符串中只出现一次并且最靠前的那个字符的位置
- (NSInteger)indexForFirstDisguistCharIn:(NSString*)str {
if (str == nil || str.length == 0) {
return -1;
}
if (str.length == 1) {
return 0;
}
// str : str's count 存储字符->字符出现的数量
NSMutableDictionary *charCountDict = [NSMutableDictionary dictionaryWithCapacity:str.length];
// 只出现1次的字符
NSMutableSet *difficultSet = [NSMutableSet setWithCapacity:str.length];
for (int i = 0; i < str.length; i++) {
NSString *subStr = [str substringWithRange:NSMakeRange(i, 1)];
if ([charCountDict.allKeys containsObject:subStr]) {
int count = [charCountDict[subStr] intValue];
charCountDict[subStr] = @(count + 1);
}
else {
charCountDict[subStr] = @(1);
}
if ([charCountDict[subStr] intValue] == 1) {
if (![difficultSet containsObject:subStr]) {
[difficultSet addObject:subStr];
}
}
else {
if ([difficultSet containsObject:subStr]) {
[difficultSet removeObject:subStr];
}
}
}
for (int i = 0; i < str.length; i++) {
NSString *subStr = [str substringWithRange:NSMakeRange(i, 1)];
if ([difficultSet containsObject:subStr]) {
return i;
}
}
return -1;
}
返回正整数N的二进制周期,如果没有周期就返回-1
func getBinaryPeriodForInt(_ n: Int) -> Int {
var nn = n
var d = [Int]()
var l = 0, res = -1
while l > 0 {
d[l] = nn % 2
nn /= 2
l += 1
}
for p in 1..<l {
if p < l / 2 {
var ok = true
for i in 0..<l-p {
if d[i] != d[i+p] {
ok = false
break
}
}
if ok {
res = p
}
}
}
return res
}
选择排序、冒泡排序、插入排序
都将数组分为已排序部分和未排序部分
选择排序
将已排序部分定义在左端,然后选择未排序部分的最小元素和未排序部分的第一个元素交换。
/**
* 【选择排序】:最值出现在起始端
*
* 第1趟:在n个数中找到最小(大)数与第一个数交换位置
* 第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置
* 重复这样的操作...依次与第三个、第四个...数交换位置
* 第n-1趟,最终可实现数据的升序(降序)排列。
*
*/
void selectSort(int *arr, int length) {
for (int i = 0; i < length - 1; i++) { //趟数
for (int j = i + 1; j < length; j++) { //比较次数
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
冒泡排序
将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
/**
* 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
* 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置
* 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
* …… ……
* 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置
*/
void bublleSort(int *arr, int length) {
for(int i = 0; i < length - 1; i++) { //趟数
for(int j = 0; j < length - i - 1; j++) { //比较次数
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
插入排序
将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。
/**
* 折半查找:优化查找时间(不用遍历全部数据)
*
* 折半查找的原理:
* 1> 数组必须是有序的
* 2> 必须已知min和max(知道范围)
* 3> 动态计算mid的值,取出mid对应的值进行比较
* 4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1
* 5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1
*
*/
// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置
int findKey(int *arr, int length, int key) {
int min = 0, max = length - 1, mid;
while (min <= max) {
mid = (min + max) / 2; //计算中间值
if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
二分查找
折半查找:优化查找时间(不用遍历全部数据)
折半查找的原理
- 1> 数组必须是有序的
- 2> 必须已知min和max(知道范围)
- 3> 动态计算mid的值,取出mid对应的值进行比较
- 4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1
- 5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1
// 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置
int findKey(int *arr, int length, int key) {
int min = 0, max = length - 1, mid;
while (min <= max) {
mid = (min + max) / 2; //计算中间值
if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
将一个字符串进行反转(考虑系统表情符号)
- (NSString*)reverseString:(NSString*)str {
NSMutableString *reverString = [NSMutableString stringWithCapacity:str.length];
NSMutableArray *strRanges = [NSMutableArray arrayWithCapacity:str.length];
NSRange range;
for(NSInteger i = 0; i < str.length; i += range.length) {
range = [str rangeOfComposedCharacterSequenceAtIndex:i];
[strRanges addObject:[NSValue valueWithRange:NSMakeRange(i, range.length)]];
}
for (NSInteger i = strRanges.count - 1; i >= 0; i -= 1) {
range = [[strRanges objectAtIndex:i] rangeValue];
[reverString appendString:[str substringWithRange:range]];
}
return [reverString copy];
}
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字
- (NSInteger)duplicateInArray:(NSMutableArray<NSNumber*>*)array {
if (array == nil || array.count == 0) {
return -1;
}
for (int i = 0; i < array.count; i++) {
NSInteger valueAtI = [[array objectAtIndex:i] integerValue];
while (valueAtI != i) {
NSInteger valueAtArrayI = [[array objectAtIndex:valueAtI] integerValue];
NSLog(@"----%d valueAtArrayI %ld = %ld",i,valueAtI,valueAtArrayI);
if (valueAtI == valueAtArrayI) {
return valueAtI;
}
[array exchangeObjectAtIndex:i withObjectAtIndex:valueAtI];
valueAtI = valueAtArrayI;
}
}
return -1;
}
LRU算法是否了解,如何实现一套LRU算法?
LRU(Least recently used 最近最少使用)算法是一个缓存淘汰算法,其作用就是当缓存很多时,该淘汰哪些内容,见名知意,它的核心思想是淘汰最近使用最少的内容。实现它的关键步骤是:
- 新数据插入到链表的头部
- 每当缓存命中时,则将数据移动到链表头部
- 链表满时,将尾部数据清除
面试中出现过的算法
判断一颗二叉树是否是平衡二叉树
手写LFU
求N!
-
字符串转整形
-
反转链表(递归和非递归)
-
将两个有序链表合并成一个有序链表
-
给定一个数字n 求出全部集合(n = 3 输出 [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]])
-
Lc 200 求岛屿个数
-
判断一个字符串是不是对称的字符串,比如 abba 或者 aba 这样的就是对称的
-
二叉树逐层打印
-
判断一个字符串是不是 ipv6 地址
-
找出一个页面中漏出部分面积最大的试图,重合的部分按照最上层的面积算漏出,会有时间复杂度的要求。
-
一个坦克从一个空间的起点到终点,中间在某些位置上有阻隔的情况下,判断从起点到终点是否有可行路径的算法题。
-
二叉树翻转
-
每K个节点翻转链表
-
顺时针打印矩阵
-
自实现pow(double, double)
-
两个排序数组的中位数