diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c57cdaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/C/cmake-build-debug/ +/C/build/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/algo_notes.iml b/.idea/algo_notes.iml new file mode 100644 index 0000000..bc2cd87 --- /dev/null +++ b/.idea/algo_notes.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/liuzhuangzhuang01.xml b/.idea/dictionaries/liuzhuangzhuang01.xml new file mode 100644 index 0000000..ab315ff --- /dev/null +++ b/.idea/dictionaries/liuzhuangzhuang01.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..748881e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/C/.idea/.gitignore b/C/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/C/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/C/.idea/C.iml b/C/.idea/C.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/C/.idea/C.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/C/.idea/dictionaries/zhzhliu.xml b/C/.idea/dictionaries/zhzhliu.xml new file mode 100644 index 0000000..8604c7d --- /dev/null +++ b/C/.idea/dictionaries/zhzhliu.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/C/.idea/inspectionProfiles/Project_Default.xml b/C/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/C/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/C/.idea/misc.xml b/C/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/C/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/C/.idea/modules.xml b/C/.idea/modules.xml new file mode 100644 index 0000000..a0bac1c --- /dev/null +++ b/C/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/C/.idea/vcs.xml b/C/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/C/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/C/.vscode/launch.json b/C/.vscode/launch.json new file mode 100644 index 0000000..424bd06 --- /dev/null +++ b/C/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) 启动", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/a.out", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/C/.vscode/tasks.json b/C/.vscode/tasks.json new file mode 100644 index 0000000..3e5231f --- /dev/null +++ b/C/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: clang 生成活动文件", + "command": "/usr/bin/clang", + "args": [ + "-fcolor-diagnostics", + "-fansi-escape-codes", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "调试器生成的任务。" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/C/CMakeLists.txt b/C/CMakeLists.txt new file mode 100644 index 0000000..8b61673 --- /dev/null +++ b/C/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.23) +project(C C) + +set(CMAKE_C_STANDARD 11) + +add_executable(C + linked_list/main.c linked_list/List.h linked_list/List.c linked_list/main.c) \ No newline at end of file diff --git a/C/build/.cmake/api/v1/query/client-vscode/query.json b/C/build/.cmake/api/v1/query/client-vscode/query.json new file mode 100644 index 0000000..82bb964 --- /dev/null +++ b/C/build/.cmake/api/v1/query/client-vscode/query.json @@ -0,0 +1 @@ +{"requests":[{"kind":"cache","version":2},{"kind":"codemodel","version":2},{"kind":"toolchains","version":1},{"kind":"cmakeFiles","version":1}]} \ No newline at end of file diff --git a/C/build/CMakeCache.txt b/C/build/CMakeCache.txt new file mode 100644 index 0000000..a6c86e1 --- /dev/null +++ b/C/build/CMakeCache.txt @@ -0,0 +1,65 @@ +# This is the CMakeCache file. +# For build in directory: /Users/zhzhliu/Projects/Github/algo_notes/C/build +# It was generated by CMake: /Applications/CMake.app/Contents/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//No help, variable specified on the command line. +CMAKE_BUILD_TYPE:STRING=Debug + +//No help, variable specified on the command line. +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/clang++ + +//No help, variable specified on the command line. +CMAKE_C_COMPILER:FILEPATH=/usr/bin/clang + +//No help, variable specified on the command line. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE + + +######################## +# INTERNAL cache entries +######################## + +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/Users/zhzhliu/Projects/Github/algo_notes/C/build +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=20 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=2 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/Applications/CMake.app/Contents/bin/ctest +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Ninja +//Generator instance identifier. +CMAKE_GENERATOR_INSTANCE:INTERNAL= +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/Users/zhzhliu/Projects/Github/algo_notes/C +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/Applications/CMake.app/Contents/share/cmake-3.20 + diff --git a/C/build/CMakeFiles/cmake.check_cache b/C/build/CMakeFiles/cmake.check_cache new file mode 100644 index 0000000..3dccd73 --- /dev/null +++ b/C/build/CMakeFiles/cmake.check_cache @@ -0,0 +1 @@ +# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/C/linked_list/List.c b/C/linked_list/List.c new file mode 100644 index 0000000..ab3de34 --- /dev/null +++ b/C/linked_list/List.c @@ -0,0 +1,152 @@ +#include +#include +#include "List.h" + +Nodeptr talloc(void) { + return (Nodeptr) malloc(sizeof(Node)); +} + +Nodeptr makeNode(int val) { + Nodeptr node = talloc(); + node->val = val; + node->next = NULL; + return node; +} + +void freeList(Nodeptr list) { + Nodeptr position, tmp; + + position = list; + while (position != NULL) { + tmp = position->next; + free(position); + position = tmp; + } +} + +Nodeptr addAtHead(Nodeptr head, Nodeptr node) { + if (!head) { + return node; + } + node->next = head; + return node; +} + +Nodeptr addAtIndex(Nodeptr list, Nodeptr node, int index) { + // index should not less than 0 + if (index < 0) { + fprintf(stderr, "index less than 0\n"); + return list; + } + // new node should not be NULL + if (!node) { + return list; + } + // list should not be NULL + if (!list) { + return list; + } + Nodeptr cur = list; + // insert at head + if (index == 0 && cur->next == NULL) { + node->next = cur; + cur = node; + list = cur; + return list; + } + int curIdx = 0; + // traverse the list + while (cur && (curIdx <= index)) { + if (curIdx == index) { + node->next = cur->next; + cur->next = node; + break; + } + cur = cur->next; + curIdx++; + } + return list; +} + +int isEmpty(Nodeptr list) { + return list->next == NULL; +} + +int elementAtIndex(Nodeptr list, int index) { + return -1; +} + +Nodeptr deleteNodeAtIndex(Nodeptr list, int index) { + if (!list) { + return list; + } + if (index < 0) { + fprintf(stderr, "index out of bound\n"); + return list; + } + Nodeptr pre = NULL; + Nodeptr cur = list; + int curIdx = 0; + while (cur) { + if (curIdx == index) { + if (!pre) { + Nodeptr deleted = cur; + cur = cur->next; + list = cur; + free(deleted); + deleted = NULL; + } else { + Nodeptr deleted = cur; + pre->next = cur->next; + cur->next = NULL; + free(deleted); + deleted = NULL; + } + break; + } + pre = cur; + cur = cur->next; + curIdx++; + if (curIdx > index) { + break; + } + } + return list; +} + +Nodeptr appendNode(Nodeptr head, Nodeptr node) { + if (!head) { + return node; + } + Nodeptr next = head->next; + Nodeptr prev = head; + while (next) { + prev = next; + next = next->next; + } + prev->next = node; + return head; + +} + +void printList(Nodeptr list) { + if (!list) { + printf("NULL\n"); + return; + } + Nodeptr cur = list; + printf("%d->", cur->val); + printList(cur->next); +} + +void printNode(Nodeptr head) { + if (!head) + return; + printf("%d", head->val); + Nodeptr next = head->next; + while (next) { + printf("->%d", next->val); + next = next->next; + } + printf("->NULL\n"); +} diff --git a/C/linked_list/List.h b/C/linked_list/List.h new file mode 100644 index 0000000..608bb41 --- /dev/null +++ b/C/linked_list/List.h @@ -0,0 +1,27 @@ +#ifndef _List_H + +typedef struct node_t* Nodeptr; + +struct node_t { + int val; + struct node_t *next; +}; + +typedef Nodeptr List; +typedef Nodeptr Position; + +Nodeptr makeNode(int val); +Nodeptr appendNode(Nodeptr head, Nodeptr node); +Nodeptr addAtHead(Nodeptr head, Nodeptr node); +Nodeptr addAtIndex(Nodeptr list, Nodeptr node, int index); +Nodeptr deleteNodeAtIndex(Nodeptr list, int index); +void printList(Nodeptr list); +void freeList(Nodeptr list); + +int isEmpty(Nodeptr list); +int isLast(Nodeptr list); +int isList(Position p, List l); +List makeList(List list); +void deleteList(List list); + +#endif diff --git a/C/linked_list/Makefile b/C/linked_list/Makefile new file mode 100644 index 0000000..e046dc2 --- /dev/null +++ b/C/linked_list/Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -g -c -Wall + +all: main +main: main.o list.o + $(CC) -g main.o list.o -o main + +list.o: list.c + $(CC) $(CFLAGS) list.c + +clean: + rm *o main + +run: + ./main \ No newline at end of file diff --git a/C/linked_list/main.c b/C/linked_list/main.c new file mode 100644 index 0000000..c302c52 --- /dev/null +++ b/C/linked_list/main.c @@ -0,0 +1,40 @@ +// +// Created by zhzh liu on 2022/10/16. +// +#include +#include "List.h" + +int main() { + Nodeptr node = makeNode(0); + Nodeptr second = makeNode(2); + Nodeptr third = makeNode(3); + Nodeptr forth = makeNode(4); + Nodeptr list = node; + appendNode(list, second); + appendNode(list, third); + list = addAtHead(list, forth); + Nodeptr fifth = makeNode(5); + list = addAtIndex(list, fifth, 0); + printList(list); + Nodeptr sixth = makeNode(6); + list = addAtIndex(list, sixth, 3); + printList(list); + Nodeptr seventh = makeNode(7); + list = addAtIndex(list, seventh, 100); + printList(list); + + // test delete + // an empty list + printf("test delete node\n"); + Nodeptr emptylist = deleteNodeAtIndex(list, 0); + printList(emptylist); + + deleteNodeAtIndex(emptylist, 3); + printList(emptylist); + printf("-----------end---------\n"); + + printf("before free: "); + printList(emptylist); + freeList(emptylist); + printList(emptylist); +} \ No newline at end of file diff --git a/C/main.c b/C/main.c new file mode 100644 index 0000000..232e088 --- /dev/null +++ b/C/main.c @@ -0,0 +1,7 @@ +#include +#include +#include "linked_list/List.h" + +int main(int argc, char **argv) { + return 0; +} \ No newline at end of file diff --git a/interview.org b/interview.org index c7688cc..61ed2eb 100644 --- a/interview.org +++ b/interview.org @@ -667,7 +667,7 @@ 用户点击屏幕 -> UIApplication -> UIWindow.hitTest:withEvent: -> View.hitTest:withEvent. hitTest方法调用pointInside:withEvent:方法来确定那个 子视图应该响应事件。 - + *** 修改响应链 可以通过重写next属性修改响应链。UIKit中next的实现 * UIView, 如果这个view是ViewController的根视图,那么他的next是 @@ -902,7 +902,7 @@ 里包含和初始消息和参数。 我们可以使用forwardInvocation方法来处理消息转发。 - + [[https://www.jianshu.com/p/b34ecdcf8c25][OC运行时机制Runtime(二):探索Runtime的消息转发机制 - 简书]] ***** 调用机制 OC中,调用方法叫做发送消息,基本形式为 ~[receiver message]~ ; 编译器会将上 @@ -931,7 +931,7 @@ struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; struct objc_method_list *class_methods OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; - } + } #+END_SRC 包含分类名字,类名,实例方法列表,类方法列表,实现的协议列表。一般分类不能 直接添加属性,但可以使用运行时动态的给类添加属性。又叫关联对象。 @@ -951,7 +951,7 @@ * ObjectAssociation ****** AssociationsManager - 内部只有一个 AssociationsHashmap单例,使用自旋锁保证同时只有一个线程能够 + 内部只有一个 AssociationsHashMap单例,使用自旋锁保证同时只有一个线程能够 访问AssociationsHashMap,保证线程安全。在初始化的时候加锁,析构的时候解锁。 ****** ObjectAssociation 关联对象的实际存储结构 @@ -977,7 +977,7 @@ **** Runloop 概念 runloop实际上是一个对象,这个对象管理了其需要处理的事件和消息,并提供一个入 口函数来指向上面的事件循环的逻辑。线程执行了这个函数后,就会一直处于这个函 - 数内部,“接收消息-等待-处理-”的循环中,这道这个循环结束 (比如传入quit的消 + 数内部,“接收消息-等待-处理-”的循环中,直到这个循环结束 (比如传入quit的消 息),函数返回。 OSX/iOS系统中提供了两个这样的对象:NSRunloop 和 CFRunLoopRef。CFRunloopRef @@ -1008,7 +1008,7 @@ ***** CFRunLoopSourceRef 是事件产生的地方,Source有两个版本,Source0和Source1 - + ****** Source0 只包含了一个回调,只能由应用发起和处理。使用时需要调用 CFRunLoopSourceSignal(source),将这个souorce标记为待处理,然后手动调用 @@ -1041,7 +1041,7 @@ CFMutableArrayRef _timers; // Array ... }; - + struct __CFRunLoop { CFMutableSetRef _commonModes; // Set CFMutableSetRef _commonModeItems; // Set @@ -1057,12 +1057,12 @@ **** Runloop的内部逻辑 Runloop是这样一个函数,其内部是一个do-while循环。当你调用CFRunLoopRun()时, 线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。 - + **** 苹果用 RunLoop 实现的功能 App启动后,系统默认注册了5个mode: 1. kCFRunLoopDefaultMode: App的默认mode,通常主线程是在这个mode下运行的。 2. UITrackingRunLoopMode: 界面跟踪 mode, 用于scrollview追踪触摸滑动,保证 - 洁面滑动谁不受其他mode影响。 + 页面滑动时不受其他mode影响。 3. UIInitializationRunLoopMode:在刚启动App时进入的第一个mode,启动完成后不 再使用。 4. GSEventReceiveRunLoopMode:接收系统事件内部mode,通常用不到 @@ -1082,7 +1082,7 @@ 来释放自动释放池.这个observer的优先级最低,保证其释放池发生在其他所有回调之 后. - 在主线程执行代码,通过写在诸如时间回调、timer回调内.这些回调会被runloop穿件 + 在主线程执行代码,通过写在诸如时间回调、timer回调内.这些回调会被runloop创建 好的AutoreleasePool环绕着,所以不会出现内存泄漏,开发者也不必创建pool了. ***** 事件响应 @@ -1091,10 +1091,10 @@ 当一个硬件事件(触摸/锁屏/晃动等)发生后,首先有IOKit.framework生成一个 IOHIDEvent事件并有SpringBoard接收.SprintBoard只接收按键,触摸,加速,接近传感 - 器等集中Event,随后用哪个mach port转发给需要的app进程.随后苹果注册那个 + 器等几种Event,随后用哪个mach port转发给需要的app进程.随后苹果注册那个 source1事件触发回调,并调用_UIApplicationHandleEventQueue() 进行应用内部的 分发. - + _UIApplicationHandleEventQueue() 会把IOHIDEvent处理并包装成UIEvent进行处理 或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给UIWindow等,通常事件比如 UIButton点击,touchesBegin/Move/End/Cancel事件都在在这个回调中完成的. @@ -1178,7 +1178,7 @@ **** Weak原理分析 ***** weak指针帮我们干了啥 程序运行时将弱引用存入到一个hash表中,当对象要销毁的时候,哈希函数根据对 - 象地址找到索引,然后从哈希表中去除对象对应的弱指针集合,挨个清空。 + 象地址找到索引,然后从哈希表中去除对象对应的弱指针集合,挨个清空。 ***** 调用栈 #+BEGIN_SRC text -(void)dealloc -> @@ -1222,7 +1222,7 @@ magic_t const magic; id *next; pthread_t const thread; - AutoreleasePoolPage * const parent; + AutoreleasePoolPage *const parent; AutoreleasePoolPage *child; uint32_t const depth; uint32_t hiwat; @@ -1232,7 +1232,9 @@ 的 形式组合而成。 * AutoreleasePool是按线程一一对应的 * AutoreleasePoolPage的每个对象会开辟4096字节的内存,除了自己本身的实例变量 - 所占的空间,剩下的空间全部用来存储autorelease对象。 + 所占的空间,剩下的空间全部用来存储autorelease对象。值为PAGE_MAX_SIZE,4096 + 个字节,其中56个字节用来存储自己的变量,剩下的4040个字节用来存储要释放的对象,也 + 就是最多505个对象。 * ~id *next~ 指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置 * 一个AutorealsePoolPage被占满时,会新建一个AutoReleasePoolPage对象,连接链 表,后来的autorelease对象加入的新的page中。 @@ -1249,7 +1251,7 @@ objc_autoreleasePoolPop(哨兵)作为入参: 1. 根据传入的哨兵对象地址找到哨兵对象所处的page 2. 在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次 release - 消息,并向会移动指针,可以向前跨越若干个page,直到哨兵所在的位置。 + 消息,并向后移动指针,可以向前跨越若干个page,直到哨兵所在的位置。 **** POOL_SENTIEL 对象 上边提到的哨兵对象,实际上是POOL_SENTIEL对象,值为nil,定义如下: @@ -1269,10 +1271,10 @@ return 0; } #+END_SRC - - 当objc_atoreleasePoolPop调用是,会想自动释放池中的对象发送release消息,直到 + + 当objc_atoreleasePoolPop调用是,会向自动释放池中的对象发送release消息,直到 第一个POOL_SENTIEL。 - + *** Associated Objects的实现原理 :原理: [[关联对象]] [[http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/][Objective-C Associated Objects 的实现原理 - 雷纯锋的技术博客]] @@ -1281,9 +1283,9 @@ [[https://halfrost.com/ios_block/][深入研究 Block 捕获外部变量和 __block 实现原理]] OC中有三种block * _NSConcreteGlobalBlock 没有外界变量或只用到全局变量、静态变量的 - block为_NSConcreteGlobalBlock,声明周期从创建到应用程序结束。 + block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。 * _NSConcreteStackBlock 只有外部局部变量、成员变量,且没有强指针引用的block - 都是StackBlock。StackBlock的声明周期由系统控制,一旦返回之后,就被销毁了。 + 都是StackBlock。StackBlock的生命周期由系统控制,一旦返回之后,就被销毁了。 * _NSConcreteMallocBlock 有强指针引用或copy修饰的成员属性引用的block会被复制 一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期有程序员控制 **** 实现方式 @@ -1351,7 +1353,7 @@ * (__bridge_transfer CFType) expression * (__bridge type) expression CF对象必须使用CFRetain和CFRelease进行内存管理。 - 使用OBJC对象和CF对象相互转换的时候,必须让编译器直到,到底由谁来负责释放对象, + 使用OBJC对象和CF对象相互转换的时候,必须让编译器知道到底由谁来负责释放对象, 是否交给ARC处理,只有正确的处理,才能避免内存泄漏和过度释放导致的程序崩溃。 **** __bridge_retain __bridge_retain等同于 CFBridgeRetain()。 @@ -1434,7 +1436,7 @@ #+END_SRC *** 原子操作是否是线程安全的 [[http://liuduo.me/2018/02/08/objective-c-atomic/][Objective-C 原子属性 | Knowledge Library]] - + 原子操作不是线程安全的,自保证setter和getter存取方法的线程安全,并不保证整个 对象的线程安全。例如一个数组对象,如果一个线程循环的读取内存,另一个线程循环 的写内存,就肯定会产生内存问题。最好的方法还是加锁。 @@ -1464,7 +1466,7 @@ [UIView view]; //返回id类型 #+END_SRC - + 调用view方法就返回一个id类型,如果我们使用instancetype作为返回值,就可以得 到一个UIView*的类型 #+BEGIN_SRC objc @@ -1484,6 +1486,12 @@ ** Swift *** TODO enum关联对象是如何实现的 *** TODO ABI稳定意味这什么 +**** ABI是什么 +**** 稳定的含义 + Swift5.0版本开始ABI稳定,意味着编译后的Swift程序能够与操作系统和其他库的二进制接口以一种稳定的方式进行交互 + 而不受Swift版本更新的影响。 +**** 稳定之后的影响 + *** TODO 编译pipeline [[https://forums.swift.org/t/what-should-i-learn-if-i-want-to-contribute-to-the-swift-compiler/18144/5?u=dvlprliu][Compiling pipeline]] *** 性能优化 :优化: @@ -1496,7 +1504,7 @@ 此引用的值已存在的实体本身而不是拷贝. *** 值类型写时复制 * 只有当一个值发生写入行为时,才会有复制行为 - * 在结构提内部用一个引用类型来存储实际数据,再不进行写入操作的普通传递过程中, + * 在结构内部用一个引用类型来存储实际数据,再不进行写入操作的普通传递过程中, 都是将内部引用计数+1, 在进行写入操作时,对内部的引用做一次copy操作来存储新 的数据,防止和之前引用产生意外的数据共享. * 有一个 isKnownUniquelyReferenced 函数,它能检查一个类的实例是不是唯一的引用,如 @@ -1555,7 +1563,7 @@ 误,defer块里的代码都会执行. * defer块中的代码,会在当前作用于结束前调用,每当一个作用域结束就进行该作用的 defer执行. - * 如果一个作用域里有多个defer,他们将按相反的顺序执行,可以他们当作一个栈. + * 如果一个作用域里有多个defer,他们将按相反的顺序执行(先进后出),可以他们当作一个栈. #+BEGIN_SRC swift func doSomethingFile{ openDirectory() @@ -1703,7 +1711,7 @@ 任何一个对象都有一个指向side table的指针,这个side table也存在一个指针指回 到该对象,side table可以存储其他的一些信息,比如关联对象。 - 出事状态下,对象的第一个字(word)存储类信息,接下来的一个字存储引用计数。 + 初始状态下,对象的第一个字(word)存储类信息,接下来的一个字存储引用计数。 当这个对象需要side table的时候,第二个字就会被替换为指向side table的指针, 同时引用计数的信息也会存进side table。这两种情况通过设置该字中的一个比特位 来进行区分。 @@ -1738,17 +1746,17 @@ let users = [eric, maeve, otis] #+END_SRC - + 我们要取出所有User的name属性,以前需要这样: #+BEGIN_SRC swift let userNames = users.map { $0.name } #+END_SRC - + 引入SE-0239后,keypath表达式可以作为函数,可以讲上面的语句简化。 #+BEGIN_SRC swift let userNames = users.map(\.name) #+END_SRC - + 同样可以过滤出所有可以投票的用户 #+BEGIN_SRC swift let voters = users.filter(\.canVote) @@ -1769,7 +1777,7 @@ } } #+END_SRC - + 我们可以这样 #+BEGIN_SRC swift let d6 = Dice(lowerBound: 1, upperBound: 6) @@ -1779,7 +1787,7 @@ 我们可以定义callAsFunction方法,任意多个参数,返回值类型,同时也可以对 callAsFunction进行重载。 - + **** Subscripts can now declare default arguments 下标函数可以定义默认值,在访问数组越界的时候返回。 定义: @@ -1849,6 +1857,17 @@ *** 流模式与数据报模式 *** TCP保证过数据正确性,UDP可能丢包 *** TCP保证数据舜秀,UDP不保证 +** TCP建立连接的过程 +*** 三次握手建立连接 +**** 第一次握手(SYN=1, seq=x): +客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。 +发送完毕后,客户端进入 SYN_SEND 状态。 +**** 第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1): +服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。 +**** 第三次握手(ACK=1,ACKnum=y+1) +客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1 +发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。 +*** 四次挥手关闭连接 ** 七层模型 ** HTTP2的特性 [[https://zhuanlan.zhihu.com/p/26559480][一文读懂 HTTP/2 特性 - 知乎]] @@ -1940,7 +1959,7 @@ 因为_prev对象和_next对象都会被LinkedMap的_dic强引用,所以这里使用 ~__unsafe_unreatined~ 是安全的。 - + ******* _YYLinkedMap #+BEGIN_SRC objc /** @@ -1963,7 +1982,7 @@ 对于LRU缓存,一个常用的优化思路便是使用哈希表,记录每个数据的位置,将缓 存访问的时间复杂度降低到O(1)。在YYCache中没有使用NSDictionary而是使用了 - CoreFoundation提供的CFMutableDictionary,我理解这么做是出于效率的考虑, + CoreFoundation提供的CFMutableDictionary,这么做是出于效率的考虑, CoreFoundation提供了更为底层的C API,不需要经历objc runtime发消息的流程, 能够使效率更加高效。带来的影响就是需要更为小心的维护内存,避免内存泄漏。 @@ -2015,12 +2034,12 @@ 生产者消费者问题,实际上主要是包含两个类现场,一种是生产者线程用于生产数据, 另一种是消费者线程,用于消费数据,为了解耦生产者和消费者的关系,通常会采用共 享的数据区域,就像一个仓库,生产者生产数据之后直接放置在共享数据区中,并不关 - 系消费者的行为。消费中只需从共享数据区中区获取数据,就不再需要关心生产者的行 + 心消费者的行为。消费中只需从共享数据区中区获取数据,就不再需要关心生产者的行 为。共享数据区中应该具备这样线程间并发写作的功能。 1. 如果共享数据区已满,阻塞生产者继续生产数据放置入内。 - 2. 入股共享数据为空的话,阻塞消费者继续消费数据。 -** 线程池 - 单线程池 多线程池 + 2. 如果共享数据为空的话,阻塞消费者继续消费数据。 +** 线程池 + 单线程池多线程池 *** 使用线程池的目的 1. 线程是稀缺资源,不能频繁的创建。 2. 解耦作用;线程的创建于执行完全分开,方便维护。 diff --git a/src/.gitignore b/src/.gitignore index 12f997b..0896d47 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -14,6 +14,7 @@ xcuserdata/ *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +.build/* build/ DerivedData/ *.moved-aside @@ -34,6 +35,8 @@ DerivedData/ *.xcworkspace/xcuserdata/ /*.gcno +src/.build/* + ### Xcode Patch ### **/xcshareddata/WorkspaceSettings.xcsettings diff --git a/src/LRU/.vscode/launch.json b/src/LRU/.vscode/launch.json new file mode 100644 index 0000000..aa4a0b7 --- /dev/null +++ b/src/LRU/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/.build/x86_64-apple-macosx/debug/LRU.build/LRU.swift.o", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/LRU/Package.swift b/src/LRU/Package.swift index db6d2cd..270eca1 100644 --- a/src/LRU/Package.swift +++ b/src/LRU/Package.swift @@ -9,20 +9,20 @@ let package = Package( // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "LRU", - targets: ["LRU"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), + targets: ["LRU"] + ), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "LRU", - dependencies: []), + path: "Sources" + ), .testTarget( name: "LRUTests", - dependencies: ["LRU"]), + dependencies: ["LRU"], + path: "Tests" + ), ] ) diff --git a/src/LRU/Sources/LRU.swift b/src/LRU/Sources/LRU.swift new file mode 100644 index 0000000..269ca30 --- /dev/null +++ b/src/LRU/Sources/LRU.swift @@ -0,0 +1,83 @@ + +public class LRUCache { + private let capacity: Int + private var count: Int = 0 + private var values: [Int: Container] = [:] + private unowned(unsafe) var head: Container? + private unowned(unsafe) var tail: Container? + + public init(_ capacity: Int) { + self.capacity = capacity + } + + public func get(_ key: Int) -> Int { + guard let container = values[key] else { + return -1 + } + + let value = container.value + remove(container) + append(container) + return value + } + + public func put(_ key: Int, _ value: Int) { + // 检查是否已经存在该值 + if let container = values[key] { + container.value = value + remove(container) + append(container) + } else { + let container = Container(key, value) + values[key] = container + append(container) + count += 1 + } + clean() + } +} + +extension LRUCache { + class Container { + let key: Int + var value: Int + unowned(unsafe) var prev: Container? + unowned(unsafe) var next: Container? + + init(_ key: Int, _ value: Int, prev: Container? = nil, next: Container? = nil) { + self.key = key + self.value = value + self.prev = prev + self.next = next + } + } + + func remove(_ container: Container) { + if head === container { + head = container.next + } + if tail === container { + tail = container.prev + } + container.prev?.next = container.next + container.next?.prev = container.prev + container.next = nil + } + + func append(_ container: Container) { + if head == nil { + head = container + } + tail?.next = container + container.prev = tail + tail = container + } + + private func clean() { + while count > capacity, let container = head { + remove(container) + values.removeValue(forKey: container.key) + count -= 1 + } + } +} diff --git a/src/LRU/Sources/LRU/LRU.swift b/src/LRU/Sources/LRU/LRU.swift deleted file mode 100644 index ef8134e..0000000 --- a/src/LRU/Sources/LRU/LRU.swift +++ /dev/null @@ -1,57 +0,0 @@ -private class ListNode { - let val: (Int, Int) - var next: ListNode? - init(val: (Int, Int)) { - self.val = val - self.next = nil - } -} -public class LRU { - private let capacity: Int - private var list: ListNode? = ListNode(val: (-1, -1)) - private var count = 0 - init(capacity: Int) { - self.capacity = capacity - } - - func get(key: Int) -> Int? { - var cur = list - while cur != nil { - if cur?.val.0 == key { - return cur?.val.1 - } - cur = cur?.next - } - return nil - } - - func put(key: Int, value: Int) { - // 检查capacity - // 如果存满,删除最早为使用 - if count >= capacity { - list?.next = list?.next?.next - list?.next?.next = nil - } - // 如果没有存在,现在当前表中查找是否已经存在,如果已经存在,将它移到链表最后 - var cur = list - var newNode: ListNode? - while let next = cur?.next { - if next.val.0 == key { - cur?.next = next.next - newNode = next - break - } - cur = cur?.next - } - // 如果没有找到 - if newNode == nil { - newNode = ListNode(val: (key, value)) - } - while cur?.next != nil { - cur = cur?.next - } - count += 1 - cur?.next = newNode - } -} - diff --git a/src/LRU/Tests/Info.plist b/src/LRU/Tests/Info.plist new file mode 100644 index 0000000..97d40ce --- /dev/null +++ b/src/LRU/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/src/LRU/Tests/LRUTests.swift b/src/LRU/Tests/LRUTests.swift new file mode 100644 index 0000000..8e00f88 --- /dev/null +++ b/src/LRU/Tests/LRUTests.swift @@ -0,0 +1,47 @@ +import LRU +import XCTest + +final class LRUTests: XCTestCase { + typealias LRU = LRUCache + func testLRU() { + let lru = LRU(2) + lru.put(1, 1) + lru.put(2, 2) + let value = lru.get(3) + XCTAssertEqual(value, -1) + helper() + } + + func helper(file: StaticString = #file, line: UInt = #line) { + let sequence = ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"] + let params = [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] + let expects = [nil, nil, nil, 1, nil, -1, nil, -1, 3, 4] + + let mapping: [String: Any] = [ + "LRUCache": LRU.init, + "put": LRU.put, + "get": LRU.get, + ] + + var lru: LRU? + var result: Int? + + for (idx, opKey) in sequence.enumerated() { + let param: [Int] = params[idx] + let expect: Int? = expects[idx] + let op = mapping[opKey] + if let constructor = op as? ((Int) -> LRU) { + lru = constructor(param[0]) + } else if let put = op as? (LRU) -> ((Int, Int) -> Void) { + print("put(\(param[0]), \(param[1]))") + put(lru!)(param[0], param[1]) + } else if let get_ = op as? (LRU) -> ((Int) -> Int) { + print("get(\(param[0]))") + result = get_(lru!)(param[0]) + } + if let exp = expect { + XCTAssertEqual(exp, result, file: file, line: line) + } + } + } +} diff --git a/src/LRU/Tests/LRUTests/LRUTests.swift b/src/LRU/Tests/LRUTests/LRUTests.swift deleted file mode 100644 index 2a5b39b..0000000 --- a/src/LRU/Tests/LRUTests/LRUTests.swift +++ /dev/null @@ -1,25 +0,0 @@ -import XCTest -@testable import LRU - -final class LRUTests: XCTestCase { - - func testLRU() { - let lru = LRU(capacity: 10) - lru.get(key: 3) -// lru.put(key: 3) - } - - func helper() { - let sequence = ["LRUCache","put","put","get","put","get","put","get","get","get"] - let params = [[2],[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]] - let expects = [nil,nil,nil,1,nil,-1,nil,-1,3,4] - - let a = LRU.put - let mapping: [String: Any] = [ - "LRUCache": LRU.init, - "put" : LRU.put, - "get" : LRU.get, - ] - } - -} diff --git a/src/LRU/Tests/LinuxMain.swift b/src/LRU/Tests/LinuxMain.swift deleted file mode 100644 index d7cf543..0000000 --- a/src/LRU/Tests/LinuxMain.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest - -import LRUTests - -var tests = [XCTestCaseEntry]() -tests += LRUTests.allTests() -XCTMain(tests) diff --git a/src/LRU/Tests/LRUTests/XCTestManifests.swift b/src/LRU/Tests/XCTestManifests.swift similarity index 100% rename from src/LRU/Tests/LRUTests/XCTestManifests.swift rename to src/LRU/Tests/XCTestManifests.swift diff --git a/src/binary-tree/.vscode/launch.json b/src/binary-tree/.vscode/launch.json new file mode 100644 index 0000000..a797447 --- /dev/null +++ b/src/binary-tree/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/swift", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/src/binary-tree/.vscode/settings.json b/src/binary-tree/.vscode/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/binary-tree/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/binary-tree/Package.swift b/src/binary-tree/Package.swift index 0db1009..3abcc34 100644 --- a/src/binary-tree/Package.swift +++ b/src/binary-tree/Package.swift @@ -9,7 +9,8 @@ let package = Package( // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "binary-tree", - targets: ["binary-tree"]), + targets: ["binary-tree"] + ), ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -20,15 +21,22 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "binary-tree", - dependencies: []), + dependencies: [] + ), .testTarget( name: "binary-treeTests", - dependencies: ["binary-tree"]), + dependencies: ["binary-tree"], + path: "Tests/binary-treeTests" + ), .target( name: "leetcode", - dependencies: ["binary-tree"]), + dependencies: ["binary-tree"], + path: "Sources/leetcode" + ), .testTarget( name: "leetcodeTests", - dependencies: ["leetcode"]), + dependencies: ["binary-tree", "leetcode"], + path: "Tests/leetcodeTests" + ), ] ) diff --git a/src/binary-tree/Sources/binary-tree/TreeNode.swift b/src/binary-tree/Sources/binary-tree/TreeNode.swift index 1ea30c6..23aa09d 100644 --- a/src/binary-tree/Sources/binary-tree/TreeNode.swift +++ b/src/binary-tree/Sources/binary-tree/TreeNode.swift @@ -7,43 +7,79 @@ import Foundation -public class TreeNode { +public class TreeNode: ExpressibleByArrayLiteral { public let value: Value public var left: TreeNode? public var right: TreeNode? init(value: Value) { self.value = value - self.left = nil - self.right = nil + left = nil + right = nil + } + + public required init(arrayLiteral elements: Value...) { + if elements.isEmpty { fatalError() } + func buildTree(from values: [Value?], idx: Int) -> TreeNode? { + if idx >= values.count { return nil } + guard let val = values[idx] else { return nil } + let root = TreeNode(value: val) + root.left = buildTree(from: values, idx: 2 * idx + 1) + root.right = buildTree(from: values, idx: 2 * idx + 2) + return root + } + let rootIdx = 0 + value = elements[rootIdx] + left = buildTree(from: elements, idx: 2 * rootIdx + 1) + right = buildTree(from: elements, idx: 2 * rootIdx + 2) } func levelOrderTraversel() -> [Value] { - var queue = [TreeNode]() + var queue = [self] var result = [Value]() - queue.append(self) while !queue.isEmpty { - var count = queue.count - while count > 0 { + while queue.count > 0 { let top = queue.removeFirst() result.append(top.value) - count -= 1 - if let node = top.left { - queue.append(node) + if let left = top.left { + queue.append(left) } - if let node = top.right { - queue.append(node) + if let right = top.right { + queue.append(right) } } } return result } + public func levelOrderTraversel_array() -> [[Value]] { + var curLvl = [self] + var nextLvl = [TreeNode]() + var result = [[value]] + while !curLvl.isEmpty { + nextLvl = [] + for node in curLvl { + if let left = node.left { + nextLvl.append(left) + } + if let right = node.right { + nextLvl.append(right) + } + } + if !nextLvl.isEmpty { + result.append(nextLvl.map(\.value)) + } + curLvl = nextLvl + } + return result + } + func inorderTraversel_recursive() -> [Value] { (left?.inorderTraversel_recursive() ?? []) + [value] + (right?.inorderTraversel_recursive() ?? []) } + func inorderTraversel_iterate() -> [Value] { var cur: TreeNode? = self var stack = [TreeNode]() @@ -54,7 +90,9 @@ public class TreeNode { cur = cur?.left } let top = stack.popLast() - if let value = top?.value { result.append(value) } + if let value = top?.value { + result.append(value) + } cur = top?.right } return result @@ -63,6 +101,7 @@ public class TreeNode { func preorderTraversel_recursive() -> [Value] { [value] + (left?.preorderTraversel_recursive() ?? []) + (right?.preorderTraversel_recursive() ?? []) } + func preorderTraversel_iterate() -> [Value] { var result = [Value]() var stack = [self] @@ -84,6 +123,7 @@ public class TreeNode { + (right?.postorderTraversel_recursive() ?? []) + [value] } + func postorderTraversel_iterate() -> [Value] { var stack = [self] var result = [Value]() @@ -114,16 +154,15 @@ public class TreeNode { } func map(_ transform: (Value) throws -> T) rethrows -> TreeNode? { - func traverseAndMap(_ node: TreeNode?, _ transform: ((Value) throws -> T)) rethrows -> TreeNode? { + func traverseAndMap(_ node: TreeNode?, _ transform: (Value) throws -> T) rethrows -> TreeNode? { guard let node = node else { return nil } - let root = TreeNode(value: try transform(node.value)) + let root = try TreeNode(value: transform(node.value)) root.left = try traverseAndMap(node.left, transform) root.right = try traverseAndMap(node.right, transform) return root } return try traverseAndMap(self, transform) } - } extension TreeNode: CustomStringConvertible where Value: CustomStringConvertible { diff --git a/src/binary-tree/Sources/leetcode/Solution+LevelOrderTraversal.swift b/src/binary-tree/Sources/leetcode/Solution+LevelOrderTraversal.swift new file mode 100644 index 0000000..81bc646 --- /dev/null +++ b/src/binary-tree/Sources/leetcode/Solution+LevelOrderTraversal.swift @@ -0,0 +1,8 @@ +import binary_tree +import Foundation + +extension Solution { + func levelOrderTraversel_(_ root: TreeNode?) -> [[Int]] { + root?.levelOrderTraversel_array() ?? [] + } +} diff --git a/src/binary-tree/Sources/leetcode/Solution+MaxDepth.swift b/src/binary-tree/Sources/leetcode/Solution+MaxDepth.swift index 9b94046..e7fa190 100644 --- a/src/binary-tree/Sources/leetcode/Solution+MaxDepth.swift +++ b/src/binary-tree/Sources/leetcode/Solution+MaxDepth.swift @@ -45,5 +45,4 @@ extension Solution { } return depth } - } diff --git a/src/binary-tree/Sources/leetcode/Solution+Symmetric.swift b/src/binary-tree/Sources/leetcode/Solution+Symmetric.swift index 0746649..b3e6f10 100644 --- a/src/binary-tree/Sources/leetcode/Solution+Symmetric.swift +++ b/src/binary-tree/Sources/leetcode/Solution+Symmetric.swift @@ -27,6 +27,7 @@ // // https://leetcode-cn.com/problems/symmetric-tree/ +import binary_tree import Foundation extension Solution { @@ -34,28 +35,47 @@ extension Solution { func isMirror(_ p: TreeNode?, _ q: TreeNode?) -> Bool { if p == nil && q == nil { return true } if p == nil || q == nil { return false } - return p?.value == q?.value - && isMirror(p?.left, q?.right) - && isMirror(p?.right, q?.left) + return p?.value == q?.value && isMirror(p?.left, q?.right) && isMirror(p?.right, q?.left) } return isMirror(tree?.left, tree?.right) } + /// 判断tree是否是对称的 + /// - Parameter tree: 带判定的二叉树 + /// - Returns: true表明是对称的 + /// + /// 首先,我们创建一个队列,并将根节点放入队列两次。然后,我们进入一个循环,直到队列为空。在每次循环中,我们从队列中取出两个节点,分别称为 left 和 right。 + /// + /// 然后,我们进行以下判断: + /// + /// 如果 left 和 right 都是 nil,那么我们继续下一次循环。 + /// 如果 left 和 right 其中一个是 nil,那么二叉树就不对称,我们返回 false。 + /// 如果 left 和 right 的值不相等,那么二叉树就不对称,我们返回 false。 + /// 如果以上三个条件都不满足,那么我们将 left 的右子节点和 right 的左子节点,以及 left 的左子节点和 right 的右子节点,依次放入队列。 + /// + /// 这个过程会一直重复,直到队列为空。如果在整个过程中,我们没有返回 false,那么就说明这个二叉树是对称的,我们返回 true。 + /// + /// 这个函数的主要思想是,对于一个对称的二叉树,我们从根节点开始,它的左子树和右子树是镜像对称的。也就是说,左子树的左子树和右子树的右子树对称,左子树的右子树和右子树的左子树对称。这就是我们在队列中总是成对放入和取出节点的原因。 + /// + /// 在该实现中均使用数组模拟栈或者队列,在使用数组模拟的情况下,模拟栈由于都在尾部进行操作所以效率更高。 + /// + /// 模拟队列的时间复杂度: O(n2),空间复杂度O(n),n为节点个数 + /// 模拟栈的时间复杂度:O(n), 空间复杂度O(n),n为节点个数 func isSymmetric_iterate(_ tree: TreeNode?) -> Bool { - guard let node = tree else { return true } - var queue: [TreeNode?] = [] - queue.append(node) - queue.append(node) - while !queue.isEmpty { - let left = queue.removeFirst() - let right = queue.removeFirst() + guard let node = tree else { return false } + var stack = [TreeNode?]() + stack.append(node) + stack.append(node) + while !stack.isEmpty { + let left = stack.removeLast() + let right = stack.removeLast() if left == nil && right == nil { continue } if left == nil || right == nil { return false } if left?.value != right?.value { return false } - queue.append(left?.left) - queue.append(right?.right) - queue.append(left?.right) - queue.append(right?.left) + stack.append(left?.right) + stack.append(right?.left) + stack.append(left?.left) + stack.append(right?.right) } return true } diff --git a/src/binary-tree/Sources/leetcode/Solution.swift b/src/binary-tree/Sources/leetcode/Solution.swift index 0bee0a7..6fbb20c 100644 --- a/src/binary-tree/Sources/leetcode/Solution.swift +++ b/src/binary-tree/Sources/leetcode/Solution.swift @@ -5,9 +5,9 @@ // Created by zhzh liu on 4/1/20. // -import Foundation import binary_tree +import Foundation typealias TreeNode = binary_tree.TreeNode -class Solution { } +class Solution {} diff --git a/src/binary-tree/Sources/leetcode/Solution_SameTree.swift b/src/binary-tree/Sources/leetcode/Solution_SameTree.swift index bba6ba3..423fea6 100644 --- a/src/binary-tree/Sources/leetcode/Solution_SameTree.swift +++ b/src/binary-tree/Sources/leetcode/Solution_SameTree.swift @@ -38,8 +38,8 @@ // // https://leetcode-cn.com/problems/same-tree/ -import Foundation import binary_tree +import Foundation extension Solution { func sameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { diff --git a/src/binary-tree/Tests/Info.plist b/src/binary-tree/Tests/Info.plist new file mode 100644 index 0000000..97d40ce --- /dev/null +++ b/src/binary-tree/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/src/binary-tree/Tests/LinuxMain.swift b/src/binary-tree/Tests/LinuxMain.swift deleted file mode 100644 index 320f47a..0000000 --- a/src/binary-tree/Tests/LinuxMain.swift +++ /dev/null @@ -1,7 +0,0 @@ -import XCTest - -import binary_treeTests - -var tests = [XCTestCaseEntry]() -tests += binary_treeTests.allTests() -XCTMain(tests) diff --git a/src/binary-tree/Tests/XCTestManifests.swift b/src/binary-tree/Tests/XCTestManifests.swift new file mode 100644 index 0000000..2d7bac2 --- /dev/null +++ b/src/binary-tree/Tests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) + public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(binary_treeTests.allTests), + ] + } +#endif diff --git a/src/binary-tree/Tests/binary-treeTests/XCTestManifests.swift b/src/binary-tree/Tests/binary-treeTests/XCTestManifests.swift deleted file mode 100644 index 7a7012a..0000000 --- a/src/binary-tree/Tests/binary-treeTests/XCTestManifests.swift +++ /dev/null @@ -1,9 +0,0 @@ -import XCTest - -#if !canImport(ObjectiveC) -public func allTests() -> [XCTestCaseEntry] { - return [ - testCase(binary_treeTests.allTests), - ] -} -#endif diff --git a/src/binary-tree/Tests/binary-treeTests/binary_treeTests.swift b/src/binary-tree/Tests/binary-treeTests/binary_treeTests.swift index b5e4035..f09a662 100644 --- a/src/binary-tree/Tests/binary-treeTests/binary_treeTests.swift +++ b/src/binary-tree/Tests/binary-treeTests/binary_treeTests.swift @@ -1,17 +1,17 @@ -import XCTest @testable import binary_tree +import XCTest final class binary_treeTests: XCTestCase { func testLevelOrderTraversel() { - let tree = TreeNode.tree(from: [1,2,2,nil,3,nil,3]) + let tree = TreeNode.tree(from: [1, 2, 2, nil, 3, nil, 3]) let values = tree?.levelOrderTraversel() ?? [] - XCTAssertEqual([1,2,2,3,3], values.compactMap{$0}) + XCTAssertEqual([1, 2, 2, 3, 3], values.compactMap { $0 }) } func testTreeMap() { - let array = [1,2,3,4,5,6] + let array = [1, 2, 3, 4, 5, 6] let tree = array.tree() - let newTree = tree?.map({ "\($0)" })?.levelOrderTraversel() + let newTree = tree?.map { "\($0)" }?.levelOrderTraversel() XCTAssertEqual(array.map { "\($0)" }, newTree ?? []) } } diff --git a/src/binary-tree/Tests/leetcodeTests/LeetcodeTests.swift b/src/binary-tree/Tests/leetcodeTests/LeetcodeTests.swift index ab04fd2..4e09bf4 100644 --- a/src/binary-tree/Tests/leetcodeTests/LeetcodeTests.swift +++ b/src/binary-tree/Tests/leetcodeTests/LeetcodeTests.swift @@ -5,81 +5,118 @@ // Created by zhzh liu on 4/1/20. // -import XCTest @testable import binary_tree @testable import leetcode +import XCTest class LeetcodeTests: XCTestCase { + struct Case { + let value: Value + let expect: Expect - override func setUpWithError() throws { + init(_ value: Value, _ expect: Expect) { + self.value = value + self.expect = expect + } } - override func tearDownWithError() throws { - } + override func setUpWithError() throws {} + + override func tearDownWithError() throws {} func testSameTree() { let solution = Solution() - let cases: [(([Int?], [Int?]), Bool)] = [ - (([1,nil,2], [1, nil, 2]), true), - (([1,2,3], [1,2,3]), true), - (([1,2], [1, nil, 2]), false), - (([1,2,1], [1,1,2]), false), - (([], []), true) - ] + let cases = [ + (([1, nil, 2], [1, nil, 2]), true), + (([1, 2, 3], [1, 2, 3]), true), + (([1, 2], [1, nil, 2]), false), + (([1, 2, 1], [1, 1, 2]), false), + (([], []), true), + ].map(Case<([Int?], [Int?]), Bool>.init) + let methods = [ - solution.sameTree + solution.sameTree, ] - testHelper(cases: cases, methods: methods) { (test, method) -> (Bool, Bool) in - let ((a, b), ans) = test - let p = TreeNode.tree(from: a) - let q = TreeNode.tree(from: b) + testHelper(cases: cases, methods: methods) { cases, method -> Bool in + let p = TreeNode.tree(from: cases.0) + let q = TreeNode.tree(from: cases.1) let result = method(p, q) - return (ans, result) + return result } } - func testIsSymmetric() { + func testLevelOrderTraverselArray() { let solution = Solution() - let cases: [([Int?], Bool)] = [ - ([1,2,2,3,4,4,3], true), - ([1,2,2,nil,3,nil,3], false) + let cases = [ + ([3, 9, 20, nil, nil, 15, 7], [[3], [9, 20], [15, 7]]), + ([1], [[1]]), + ([], []), + ([3, 9, 20, 5, 678, 15, 7], [[3], [9, 20], [5, 678, 15, 7]]), + ].map(Case<[Int?], [[Int]]>.init) + + let methods = [ + solution.levelOrderTraversel_, ] + + testHelper(cases: cases, methods: methods) { treeData, method in + method(makeTree(treeData)) + } + } + + func testIsSymmetric() { + let solution = Solution() + let cases = [ + ([1, 2, 2, 3, 4, 4, 3], true), + ([1, 2, 2, nil, 3, nil, 3], false), + ].map(Case<[Int?], Bool>.init) + let methods = [ solution.isSymmetric_recursive, - solution.isSymmetric_iterate + solution.isSymmetric_iterate, ] - testHelper(cases: cases, methods: methods) { (test, method) -> (Bool, Bool) in - let (c, expect) = test - let tree = TreeNode.tree(from: c) - return (expect, method(tree)) + + testHelper(cases: cases, methods: methods) { cs, method -> Bool in + let tree = TreeNode.tree(from: cs) + let result = method(tree) + return result } } func testMaxDepth() { let solution = Solution() let cases = [ - ([3,9,20,nil,nil,15,7], 3), - ([1,2,3,4,5], 3) - ] + ([3, 9, 20, nil, nil, 15, 7], 3), + ([1, 2, 3, 4, 5], 3), + ].map(Case<[Int?], Int>.init) + let methods = [ solution.maxDepth_recursive, - solution.maxDepth_iterate + solution.maxDepth_iterate, ] - testHelper(cases: cases, methods: methods) { (test, method) -> (Int, Int) in - let (c, expect) = test - let tree = TreeNode.tree(from: c) - let result = method(tree) - return (expect, result) + + testHelper(cases: cases, methods: methods) { input, method -> Int in + method(makeTree(input)) } } - func testHelper(cases: [Case], methods: [Method], solution: StaticString = #function, line: UInt = #line, file: StaticString = #file, test: ((Case, Method) -> (Value, Value))) where Value: Equatable { + func testHelper( + cases: [Case], + methods: [Method], + solution: StaticString = #function, + line: UInt = #line, + file: StaticString = #file, + test: (Input, Method) -> Output + ) where Output: Equatable { for (cidx, cs) in cases.enumerated() { for (midx, method) in methods.enumerated() { - let (ans, res) = test(cs, method) - XCTAssertEqual(ans, res, "solution <\(solution)> failed for case #\(cidx): \(cs) using method #\(midx)", file: file, line: line) + let res = test(cs.value, method) + XCTAssertEqual(cs.expect, res, "solution <\(solution)> failed for case #\(cidx): \(cs) using method #\(midx)", file: file, line: line) } } } + + private func makeTree(_ values: [Int?]) -> binary_tree.TreeNode? { + TreeNode.tree(from: values) + } } diff --git a/src/linked-list/.swiftpm/xcode/xcshareddata/xcschemes/linked-list.xcscheme b/src/linked-list/.swiftpm/xcode/xcshareddata/xcschemes/linked-list.xcscheme new file mode 100644 index 0000000..04124c8 --- /dev/null +++ b/src/linked-list/.swiftpm/xcode/xcshareddata/xcschemes/linked-list.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/linked-list/Package.swift b/src/linked-list/Package.swift index 9cc3b4c..dba0255 100644 --- a/src/linked-list/Package.swift +++ b/src/linked-list/Package.swift @@ -12,8 +12,7 @@ let package = Package( targets: ["linked-list"]), ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), + .package(path: "../leetcode-testcase") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -23,6 +22,6 @@ let package = Package( dependencies: []), .testTarget( name: "linked-listTests", - dependencies: ["linked-list"]), + dependencies: ["linked-list", "leetcode-testcase"]), ] ) diff --git a/src/linked-list/Sources/linked-list/ListNode.swift b/src/linked-list/Sources/linked-list/ListNode.swift index e83883a..35ab205 100644 --- a/src/linked-list/Sources/linked-list/ListNode.swift +++ b/src/linked-list/Sources/linked-list/ListNode.swift @@ -1,6 +1,6 @@ // -// LinkedList.swift -// +// ListNode.swift +// // // Created by zhzh liu on 2022/6/25. // @@ -11,20 +11,20 @@ public class ListNode: ExpressibleByArrayLiteral { public var val: Int public var next: ListNode? init(value: Int, next: ListNode? = nil) { - self.val = value + val = value self.next = next } init?(vals: [Int]) { if vals.isEmpty { return nil } - self.val = vals[0] - self.next = ListNode(vals: Array(vals[1.. Int? { @@ -38,7 +38,8 @@ public class ListNode: ExpressibleByArrayLiteral { } } -extension ListNode { - - +extension Array where Element == Int { + func asListNode() -> ListNode? { + ListNode(vals: self) + } } diff --git a/src/linked-list/Sources/linked-list/Solution.swift b/src/linked-list/Sources/linked-list/Solution.swift index 2f2a920..f904ba0 100644 --- a/src/linked-list/Sources/linked-list/Solution.swift +++ b/src/linked-list/Sources/linked-list/Solution.swift @@ -1,6 +1,6 @@ // -// File.swift -// +// Solution.swift +// // // Created by zhzh liu on 2022/6/25. // @@ -32,7 +32,7 @@ class Solution { enum ReverseList { static func byIterate(_ head: ListNode?) -> ListNode? { if head == nil || head?.next == nil { return head } - var pre: ListNode? = nil + var pre: ListNode? var next = head?.next var head = head while let newHead = head { @@ -50,7 +50,7 @@ class Solution { if head == nil || head?.next == nil { return head } - var pre: ListNode? = nil + var pre: ListNode? var cur = head while cur != nil { let next = cur?.next @@ -70,7 +70,7 @@ class Solution { } return recur(head, pre: nil) } - + static func recursiveSimple(_ head: ListNode?) -> ListNode? { if head == nil || head?.next == nil { return head @@ -81,4 +81,53 @@ class Solution { return ret } } + + enum MergeTwoLists { + typealias Solution = (ListNode?, ListNode?) -> ListNode? + static func byIterate(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + if l1 == nil { return l2 } + if l2 == nil { return l1 } + var newList: ListNode? + var head = newList + var l1N = l1 + var l2N = l2 + while let l = l1N, let r = l2N { + var lower: ListNode? + if l.val <= r.val { + lower = l + l1N = l1N?.next + } else { + lower = r + l2N = l2N?.next + } + if newList == nil { + newList = lower + head = lower + } else { + newList?.next = lower + newList = lower + } + } + if l1N != nil { + newList?.next = l1N + } else { + newList?.next = l2N + } + return head + } + + static func byRecursive(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + if l1 == nil { + return l2 + } else if l2 == nil { + return l1 + } else if l1!.val <= l2!.val { + l1?.next = byRecursive(l1?.next, l2) + return l1 + } else { + l2?.next = byRecursive(l1, l2?.next) + return l2 + } + } + } } diff --git a/src/linked-list/Tests/linked-listTests/SolutionTests.swift b/src/linked-list/Tests/linked-listTests/SolutionTests.swift index 7afa5d8..14f8b2b 100644 --- a/src/linked-list/Tests/linked-listTests/SolutionTests.swift +++ b/src/linked-list/Tests/linked-listTests/SolutionTests.swift @@ -1,20 +1,17 @@ // // SolutionTests.swift -// +// // // Created by zhzh liu on 2022/6/25. // -import XCTest @testable import linked_list +import XCTest final class SolutionTests: XCTestCase { + override func setUpWithError() throws {} - override func setUpWithError() throws { - } - - override func tearDownWithError() throws { - } + override func tearDownWithError() throws {} func testReversePrint() { let list = ListNode(value: 2, next: ListNode(value: 1, next: ListNode(value: 3))) @@ -39,21 +36,48 @@ final class SolutionTests: XCTestCase { func testReverseList(using solution: (ListNode?) -> ListNode?, file: StaticString = #filePath, line: UInt = #line) { let cases: [(param: ListNode?, exp: ListNode?)] = [ - ([1,2,3,4,5], [5,4,3,2,1]), - ([1,2,3], [3,2,1]), + ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]), + ([1, 2, 3], [3, 2, 1]), ([1], [1]), ] for c in cases { - let result = solution(c.param) - let expect = c.exp - var check: (ListNode?, ListNode?) = (result, expect) - while let r = check.0, let e = check.1 { - XCTAssertEqual(r.val, e.val, file: file, line: line) - check.0 = check.0?.next - check.1 = check.1?.next - } - XCTAssertTrue(check.0 == nil && check.1 == nil, file: file, line: line) + XCTAssertListEqual(solution(c.param), c.exp, file: file, line: line) } } + func testMergeList() { + testMergeList(using: Solution.MergeTwoLists.byIterate) + testMergeList(using: Solution.MergeTwoLists.byRecursive) + } + + func testMergeList(using solution: Solution.MergeTwoLists.Solution, file: StaticString = #filePath, line: UInt = #line) { + let cases: [(param: ([Int], [Int]), expect: [Int])] = [ + (([1, 2, 4], [1, 3, 4]), [1, 1, 2, 3, 4, 4]), + (([1, 2, 3], []), [1, 2, 3]), + (([], [1, 2, 3]), [1, 2, 3]), + (([1], [1, 2, 3]), [1, 1, 2, 3]), + ] + for c in cases { + let l1 = c.param.0.asListNode() + let l2 = c.param.1.asListNode() + let expect = c.expect.asListNode() + XCTAssertListEqual(solution(l1, l2), expect, file: file, line: line) + } + } + + func XCTAssertListEqual( + _ list1: @autoclosure () -> ListNode?, + _ list2: @autoclosure () -> ListNode?, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line + ) { + var check: (result: ListNode?, expect: ListNode?) = (list1(), list2()) + while let r = check.result, let e = check.expect { + XCTAssertEqual(r.val, e.val, message(), file: file, line: line) + check.0 = check.0?.next + check.1 = check.1?.next + } + XCTAssertTrue(check.0 == nil && check.1 == nil, message(), file: file, line: line) + } }