1. 反轉二叉樹,不用遞歸node
1
2
3
4
5
6
7
8
9
|
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
|
遞歸方式:git
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class Solution {
public TreeNode invertTree(TreeNode root) {
if
(root ==
null
) {
return
null
;
}
root.left = invertTree(root.left);
root.right = invertTree(root.right);
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return
root;
}
}
|
Objective-C實現:github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 翻轉二叉樹(又叫:二叉樹的鏡像)
*
* @param rootNode 根節點
*
* @return 翻轉後的樹根節點(其實就是原二叉樹的根節點)
*/
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {
if
(!rootNode) {
return
nil; }
if
(!rootNode.leftNode && !rootNode.rightNode) {
return
rootNode; }
[self invertBinaryTree:rootNode.leftNode];
[self invertBinaryTree:rootNode.rightNode];
BinaryTreeNode *tempNode = rootNode.leftNode;
rootNode.leftNode = rootNode.rightNode;
rootNode.rightNode = tempNode;
return
rootNode;
}
|
非遞歸方式:算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {
if
(!rootNode) {
return
nil; }
if
(!rootNode.leftNode && !rootNode.rightNode) {
return
rootNode; }
NSMutableArray *queueArray = [NSMutableArray array];
//數組當成隊列
[queueArray addObject:rootNode];
//壓入根節點
while
(queueArray.count > 0) {
BinaryTreeNode *node = [queueArray firstObject];
[queueArray removeObjectAtIndex:0];
//彈出最前面的節點,仿照隊列先進先出原則
BinaryTreeNode *pLeft = node.leftNode;
node.leftNode = node.rightNode;
node.rightNode = pLeft;
if
(node.leftNode) {
[queueArray addObject:node.leftNode];
}
if
(node.rightNode) {
[queueArray addObject:node.rightNode];
}
}
return
rootNode;
}
|
示例代碼參考:二叉樹數組
2. 寫一個單例模式xcode
1
2
3
4
5
6
7
8
9
|
+ (AccountManager *)sharedManager
{
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return
sharedAccountManagerInstance;
}
|
3. iOS應用生命週期安全
應用程序的狀態:服務器
Not running未運行:程序沒啓動。網絡
Inactive未激活:程序在前臺運行,不過沒有接收到事件。在沒有事件處理狀況下程序一般停留在這個狀態。併發
Active激活:程序在前臺運行並且接收到了事件。這也是前臺的一個正常的模式。
Backgroud後臺:程序在後臺並且能執行代碼,大多數程序進入這個狀態後會在在這個狀態上停留一會。時間到以後會進入掛起狀態(Suspended)。有的程序通過特殊的請求後能夠長期處於Backgroud狀態。
Suspended掛起:程序在後臺不能執行代碼。系統會自動把程序變成這個狀態並且不會發出通知。當掛起時,程序仍是停留在內存中的,當系統內存低時,系統就把掛起的程序清除掉,爲前臺程序提供更多的內存。
iOS的入口在main.m文件:
1
2
3
4
5
6
|
int main(int argc, char *argv[])
{
@autoreleasepool {
return
UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
|
main函數的兩個參數,iOS中沒有用到,包括這兩個參數是爲了與標準ANSI C保持一致。 UIApplicationMain函數,前兩個和main函數同樣,重點是後兩個。
後兩個參數分別表示程序的主要類(principal class)和代理類(delegate class)。若是主要類(principal class)爲nil,將從Info.plist中獲取,若是Info.plist中不存在對應的key,則默認爲UIApplication;若是代理類(delegate class)將在新建工程時建立。
根據UIApplicationMain函數,程序將進入AppDelegate.m,這個文件是xcode新建工程時自動生成的。下面看一下AppDelegate.m文件,這個關乎着應用程序的生命週期。
一、application didFinishLaunchingWithOptions:當應用程序啓動時執行,應用程序啓動入口,只在應用程序啓動時執行一次。若用戶直接啓動,lauchOptions內無數據,若經過其餘方式啓動應用,lauchOptions包含對應方式的內容。
二、applicationWillResignActive:在應用程序將要由活動狀態切換到非活動狀態時候,要執行的委託調用,如 按下 home 按鈕,返回主屏幕,或全屏之間切換應用程序等。
三、applicationDidEnterBackground:在應用程序已進入後臺程序時,要執行的委託調用。
四、applicationWillEnterForeground:在應用程序將要進入前臺時(被激活),要執行的委託調用,恰好與applicationWillResignActive 方法相對應。
五、applicationDidBecomeActive:在應用程序已被激活後,要執行的委託調用,恰好與applicationDidEnterBackground 方法相對應。
六、applicationWillTerminate:在應用程序要徹底推出的時候,要執行的委託調用,這個須要要設置UIApplicationExitsOnSuspend的鍵值。
初次啓動:
1
2
|
iOS_didFinishLaunchingWithOptions
iOS_applicationDidBecomeActive
|
按下home鍵:
1
2
|
iOS_applicationWillResignActive
iOS_applicationDidEnterBackground
|
點擊程序圖標進入:
1
2
|
iOS_applicationWillEnterForeground
iOS_applicationDidBecomeActive
|
當應用程序進入後臺時,應該保存用戶數據或狀態信息,全部沒寫到磁盤的文件或信息,在進入後臺時,最後都寫到磁盤去,由於程序可能在後臺被殺死。釋放盡量釋放的內存。
1
|
- (void)applicationDidEnterBackground:(UIApplication *)application
|
方法有大概5秒的時間讓你完成這些任務。若是超過期間還有未完成的任務,你的程序就會被終止並且從內存中清除。
若是還須要長時間的運行任務,能夠在該方法中調用:
1
2
3
|
[application beginBackgroundTaskWithExpirationHandler:^{
NSLog(@
"begin Background Task With Expiration Handler"
);
}];
|
程序終止
程序只要符合如下狀況之一,只要進入後臺或掛起狀態就會終止:
①iOS 4.0之前的系統
②app是基於iOS 4.0以前系統開發的。
③設備不支持多任務
④在Info.plist文件中,程序包含了 UIApplicationExitsOnSuspend 鍵。
系統經常是爲其餘app啓動時因爲內存不足而回收內存最後須要終止應用程序,但有時也會是因爲app很長時間才響應而終止。若是app當時運行在後臺而且沒有暫停,系統會在應用程序終止以前調用app的代理的方法 - (void)applicationWillTerminate:(UIApplication *)application,這樣可讓你能夠作一些清理工做。你能夠保存一些數據或app的狀態。這個方法也有5秒鐘的限制。超時後方法會返回程序從內存中清除。
注意:用戶能夠手工關閉應用程序。
4. 一工人給老闆打7天工要求一塊金條 這金條只能切2次 工人天天要1/7金條 怎麼分?
這道題解決的主要難點在於:不是給出去的就收不回來了,能夠用交換的方法。
把金條分紅三段(就是分兩次,或者切兩刀),分別是整根金條的1/七、2/七、 4/7。
第一天:給1/7的, 次日:給2/7的,收回1/7的; 第三天,給1/7的; 第四天:給4/7的,收回1/7和2/7的 ;第五天:給1/7的 ;第六天:給2/7的,收回1/7的;第七天發1/7。
5. iOS中socket使用
Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。
http協議:對應於應用層
tcp協議:對應於傳輸層
ip協議:對應於網絡層
三者本質上沒有可比性。 況且HTTP協議是基於TCP鏈接的。
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要解決如何包裝數據。
我 們在傳輸數據時,能夠只使用傳輸層(TCP/IP),可是那樣的話,因爲沒有應用層,便沒法識別數據內容,若是想要使傳輸的數據有意義,則必須使用應用層 協議,應用層協議不少,有HTTP、FTP、TELNET等等,也能夠本身定義應用層協議。WEB使用HTTP做傳輸層協議,以封裝HTTP文本信息,然 後使用TCP/IP作傳輸層協議將它發送到網絡上。
SOCKET原理
一、套接字(socket)概念
套接字(socket)是通訊的基石,是支持TCP/IP協議的網絡通訊的基本操做單元。它是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
應 用層經過傳輸層進行數據通訊時,TCP會遇到同時爲多個應用程序進程提供併發服務的問題。多個TCP鏈接或多個應用程序進程可能須要經過同一個 TCP協議端口傳輸數據。爲了區別不一樣的應用程序進程和鏈接,許多計算機操做系統爲應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應 用層能夠和傳輸層經過Socket接口,區分來自不一樣應用程序進程或網絡鏈接的通訊,實現數據傳輸的併發服務。
二、創建socket鏈接
創建Socket鏈接至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket,另外一個運行於服務器端,稱爲ServerSocket。
套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。
服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態,等待客戶端的鏈接請求。
客戶端請求:指客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。
連 接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶 端,一旦客戶端確認了此描述,雙方就正式創建鏈接。而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。
三、SOCKET鏈接與TCP鏈接
建立Socket鏈接時,能夠指定使用的傳輸層協議,Socket能夠支持不一樣的傳輸層協議(TCP或UDP),當使用TCP協議進行鏈接時,該Socket鏈接就是一個TCP鏈接。
四、Socket鏈接與HTTP鏈接
由 於一般狀況下Socket鏈接就是TCP鏈接,所以Socket鏈接一旦創建,通訊雙方便可開始相互發送數據內容,直到雙方鏈接斷開。但在實際網絡應用 中,客戶端到服務器之間的通訊每每須要穿越多箇中間節點,例如路由器、網關、防火牆等,大部分防火牆默認會關閉長時間處於非活躍狀態的鏈接而致使 Socket 鏈接斷連,所以須要經過輪詢告訴網絡,該鏈接處於活躍狀態。
而HTTP鏈接使用的是「請求—響應」的方式,不只在請求時須要先創建鏈接,並且須要客戶端向服務器發出請求後,服務器端才能回覆數據。
很 多狀況下,須要服務器端主動向客戶端推送數據,保持客戶端與服務器數據的實時與同步。此時若雙方創建的是Socket鏈接,服務器就能夠直接將數據傳送給 客戶端;若雙方創建的是HTTP鏈接,則服務器須要等到客戶端發送一次請求後才能將數據傳回給客戶端,所以,客戶端定時向服務器端發送鏈接請求,不只能夠 保持在線,同時也是在「詢問」服務器是否有新的數據,若是有就將數據傳給客戶端。
6. 網絡請求中post和get的區別
GET是用於獲取數據的,POST通常用於將數據發給服務器之用。
廣泛答案
1.GET使用URL或Cookie傳參。而POST將數據放在BODY中。
2.GET的URL會有長度上的限制,則POST的數據則能夠很是大。
3.POST比GET安全,由於數據在地址欄上不可見。
不過也有文章說其實上面的是錯誤的,具體參考這篇文章。
7. 時間複雜度和空間複雜度
因爲打不出數字符號,只能貼圖了。
時間複雜度
求時間複雜度
【1】若是算法的執行時間不隨着問題規模n的增長而增加,即便算法中有上千條語句,其執行時間也不過是一個較大的常數。此類算法的時間複雜度是O(1)。
1
|
x=91; y=100;
while
(y>0)
if
(x>100) {x=x-10;y--;}
else
x++;解答: T(n)=O(1)
|
這段程序的運行是和n無關的,就算它再循環一萬年,咱們也無論他,只是一個常數階的函數。
【2】當有若干個循環語句時,算法的時間複雜度是由嵌套層數最多的循環語句中最內層語句的頻度f(n)決定的。
1
2
3
4
5
|
x=1;
for
(i=1;i<=n;i++)
for
(j=1;j<=i;j++)
for
(k=1;k<=j;k++)
x++;
|
該程序段中頻度最大的語句是(5),內循環的執行次數雖然與問題規模n沒有直接關係,可是卻與外層循環的變量取值有關,而最外層循環的次數直接與n有關,所以能夠從內層循環向外層分析語句(5)的執行次數: 則該程序段的時間複雜度爲
【3】算法的時間複雜度不只僅依賴於問題的規模,還與輸入實例的初始狀態有關。
在數值A[0..n-1]中查找給定值K的算法大體以下:
1
2
3
4
|
i=n-1;
while
(i>=0&&(A[i]!=k))
i--;
return
i;
|
此算法中的語句(3)的頻度不只與問題規模n有關,還與輸入實例中A的各元素取值及K的取值有關: ①若A中沒有與K相等的元素,則語句(3)的頻度f(n)=n; ②若A的最後一個元素等於K,則語句(3)的頻度f(n)是常數0。
空間複雜度
一個程序的空間複雜度是指運行完一個程序所需內存的大小。利用程序的空間複雜度,能夠對程序的運行所須要的內存多少有個預先估計。一個程序執行時除了須要存儲空間和存儲自己所使用的指令、常數、變量和輸入數據外,還須要一些對數據進行操做的工做單元和存儲一些爲現實計算所需信息的輔助空間。程序執行時所需存儲空間包括如下兩部分。
(1)固定部分。這部分空間的大小與輸入/輸出的數據的個數多少、數值無關。主要包括指令空間(即代碼空間)、數據空間(常量、簡單變量)等所佔的空間。這部分屬於靜態空間。
(2)可變空間,這部分空間的主要包括動態分配的空間,以及遞歸棧所需的空間等。這部分的空間大小與算法有關。
一個算法所需的存儲空間用f(n)表示。S(n)=O(f(n)) 其中n爲問題的規模,S(n)表示空間複雜度。
8. 支付寶SDK使用
使用支付寶進行一個完整的支付功能,大體有如下步驟:向支付寶申請, 與支付寶簽約,得到商戶ID(partner)和帳號ID(seller)和私鑰(privateKey)。下載支付寶SDK,生成訂單信息,簽名加密調用支付寶客戶端,由支付寶客戶端跟支付寶安全服務器打交道。支付完畢後,支付寶客戶端會自動跳回到原來的應用程序,在原來的應用程序中顯示支付結果給用戶看。
集成以後可能遇到的問題
1)集成SDK編譯時找不到 openssl/asn1.h 文件
解決方案:Build Settings --> Search Paths --> Header Search paths : $(SRCROOT)/支付寶集成/Classes/Alipay
2)連接時:找不到 SystemConfiguration.framework 這個庫
解決方案:
打開支付寶客戶端進行支付(用戶沒有安裝支付寶客戶端,直接在應用程序中添加一個WebView,經過網頁讓用戶進行支付)
1
2
|
// 注意:若是是經過網頁支付完成,那麼會回調該block:callback
[[AlipaySDK defaultService] payOrder:orderString fromScheme:@
"jingdong"
callback:^(NSDictionary *resultDic) { }];
|
在AppDelegate.m
1
2
3
|
// 當經過別的應用程序,將該應用程序打開時,會調用該方法
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options{
// 當用戶經過支付寶客戶端進行支付時,會回調該block:standbyCallback
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) { NSLog(@
"result = %@"
,resultDic); }];
return
YES;}
|
9. 遠程推送
當服務端遠程向APNS推送至一臺離線的設備時,蘋果服務器Qos組件會自動保留一份最新的通知,等設備上線後,Qos將把推送發送到目標設備上。
遠程推送的基本過程:
1.客戶端的app須要將用戶的UDID和app的bundleID發送給apns服務器,進行註冊,apns將加密後的device Token返回給app
2.app得到device Token後,上傳到公司服務器
3.當須要推送通知時,公司服務器會將推送內容和device Token一塊兒發給apns服務器
4.apns再將推送內容送到客戶端上
建立證書的流程:
1.打開鑰匙串,生成CertificateSigningRequest.certSigningRequest文件
2.將CertificateSigningRequest.certSigningRequest上傳進developer,導出.cer文件
3.利用CSR導出P12文件
4.須要準備下設備token值(無空格)
5.使用OpenSSL合成服務器所使用的推送證書
本地app代碼參考
1.註冊遠程通知
1
2
3
4
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
//中註冊遠程通知
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
}
|
2,實現幾個代理方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//獲取deviceToken令牌
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
//獲取設備的deviceToken惟一編號
NSLog(@
"deviceToken=%@"
,deviceToken);
NSString *realDeviceToken=[NSString stringWithFormat:@
"%@"
,deviceToken];
//去除<>
realDeviceToken = [realDeviceToken stringByReplacingOccurrencesOfString:@
"<"
withString:@
""
];
realDeviceToken = [realDeviceToken stringByReplacingOccurrencesOfString:@
">"
withString:@
""
];
NSLog(@
"realDeviceToken=%@"
,realDeviceToken);
[[NSUserDefaults standardUserDefaults] setValue:realDeviceToken forKey:@
"DeviceToken"
];
//要發送給服務器
}
//獲取令牌出錯
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
//註冊遠程通知設備出錯
NSLog(@
"RegisterForRemoteNotification error=%@"
,error);
}
//在應用在前臺時受到消息調用
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
//打印推送的消息
NSLog(@
"%@"
,[[userInfo objectForKey:@
"aps"
] objectForKey:@
"alert"
]):
}
|
配置後臺模式
通常咱們是使用開發版本的Provisioning作推送測試,若是沒有問題,再使用發佈版本證書的時候通常也應該是沒有問題的。爲了以防萬一,咱們能夠在越獄的手機上安裝咱們的使用發佈版證書的ipa文件(最好使用debug版本,並打印出獲取到的deviceToken),安裝成功後在;XCode->Window->Organizer-找到對應的設備查看console找到打印的deviceToken。
在後臺的推送程序中使用發佈版製做的證書並使用該deviceToken作推送服務.
使用開發和發佈證書獲取到的deviceToken是不同的。
10. @protocol 和 category 中如何使用 @property
1)在protocol中使用property只會生成setter和getter方法聲明,咱們使用屬性的目的,是但願遵照我協議的對象能實現該屬性
2)category 使用 @property 也是隻會生成setter和getter方法的聲明,若是咱們真的須要給category增長屬性的實現,須要藉助於運行時的兩個函數:
1
2
|
①objc_setAssociatedObject
②objc_getAssociatedObject
|