Yee’s Blog

Practicing Thinking Learning Sharing .

AsyncDisplayKit 源码分析

ASDisplayNode

ASDisplayNode 是该Kit的所有public class 中的base class。 ASDisplayNode 提供了与UIView 几乎同样的api接口,其又有两个辅助类。 ASDisplayNode 类中有一个用来在主线程中执行block

1
2
3
4
5
6
7
8
9
10
void ASDisplayNodePerformBlockOnMainThread(void (^block)())
{
  if ([NSThread isMainThread]) {
    block();
  } else {
    dispatch_async(dispatch_get_main_queue(), ^{
      block();
    });
  }
}

这里没有粗暴调用NSObject 执行主线程的方法。

1
2
ASDisplayNode (Subclassing)
ASDisplayNodeInternal.h //定义ASDisplayNode内部使用的方法和property。这里竟然把这些定义一个单独的头文件里。

ASDisplayNode 是异步UI绘制,对于异步并发程序,关键在于是如何设计锁,以及如何进行加锁。 ASDisplayNode 使用以下加锁的方法,那么其加锁是怎么实现的呢? ASDN::MutexLocker l(_propertyLock); 还发现编码习惯,在这些方法体中,都先对参数进行判断,如果不满足条件直接调用return,非常严谨。而且大量使用宏和断言。

1
  ASDisplayNodeAssertThreadAffinity(self);

这行代码不知其意思?????

ASDisplayNode(SubClassing)

提供 subClases of ASDisplayNode 必须后者可以被overriden 的方法。这些方法不可以用于直接调用。 通过实现+displayWithParameters:isCancelled:或者drawRect:withParameters:isCancelled 提供绘制UI;

1
-drawParametersForAsyncLayer

将涉及drawing 的所有property复制到用于display queue(异步)的immutable object。

ASControlNode

在这些方法里,都运用到了断言对参数进行判断NSParameterAssert(action) 这种编码习惯值得借鉴!

1
NSMapTable

发现一个全新的数据集合类型,之前一直没怎么用到过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask
{
  NSParameterAssert(action);
  NSParameterAssert(controlEventMask != 0);
  
  // Convert nil to [NSNull null] so that it can be used as a key for NSMapTable.
  if (!target)
    target = [NSNull null];

  // Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask
  _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^
    (ASControlNodeEvent controlEvent)
    {
      // Do we already have an event table for this control event?
      id<NSCopying> eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent);
      NSMapTable *eventDispatchTable = [_controlEventDispatchTable objectForKey:eventKey];
      // Create it if necessary.
      if (!eventDispatchTable)
      {
        // Create the dispatch table for this event.
        eventDispatchTable = [NSMapTable weakToStrongObjectsMapTable];
        [_controlEventDispatchTable setObject:eventDispatchTable forKey:eventKey];
      }

      // Have we seen this target before for this event?
      NSMutableArray *targetActions = [eventDispatchTable objectForKey:target];
      if (!targetActions)
      {
        // Nope. Create an actions array for it.
        targetActions = [[NSMutableArray alloc] initWithCapacity:kASControlNodeActionDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
        [eventDispatchTable setObject:targetActions forKey:target];
      }

      // Add the action message.
      // Note that bizarrely enough UIControl (at least according to the docs) supports duplicate target-action pairs for a particular control event, so we replicate that behavior.
      [targetActions addObject:NSStringFromSelector(action)];
    });

  self.userInteractionEnabled = YES;
}

下面这个方法是C 风格的方法,为什么要这么写呢?

1
2
3
4
5
6
7
8
9
10
void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent))
{
  // Start with our first event (touch down) and work our way up to the last event (touch cancel)
  for (ASControlNodeEvent thisEvent = ASControlNodeEventTouchDown; thisEvent <= ASControlNodeEventTouchCancel; thisEvent <<= 1)
  {
    // If it's included in the mask, invoke the block.
    if ((mask & thisEvent) == thisEvent)
      block(thisEvent);
  }
}

ASTableView

对该初始化方法进行分析,发现该类由三个主要组件构成 ASFlowLayoutController, ASRangeController, ASDataController。 这三个controller 也被用来实现ASCollectionView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{
  if (!(self = [super initWithFrame:frame style:style]))
    return nil;

  _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical];

  _rangeController = [[ASRangeController alloc] init];
  _rangeController.layoutController = _layoutController;
  _rangeController.delegate = self;

  _dataController = [[ASDataController alloc] init];
  _dataController.dataSource = self;
  _dataController.delegate = _rangeController;

  return self;
}

ASNetworkImageNode

可以通过学习这个类,来很好的研究网络下载图片的最佳实践。

1
2
3
4
5
6
7
8
9
10
11
- (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASImageDownloaderProtocol>)downloader
{
  if (!(self = [super init]))
    return nil;

  _cache = cache;
  _downloader = downloader;
  _shouldCacheImage = YES;

  return self;
}

包含一个cache, downloader, 以及一个状态标志flag。 这里需要学习的是如何实现cache,以及 downloader呢? DownLoader 是ASNetworkImageNode 里面的一个property ,也就是组合模式。 取消下载方法:

1
2
3
4
5
6
7
8
9
10
11
- (void)_cancelImageDownload
{
  if (!_imageDownload) {
    return;
  }

  [_downloader cancelImageDownloadForIdentifier:_imageDownload];
  _imageDownload = nil;

  _cacheUUID = nil;
}
1
2
3
4
5
6
7
8
9
10
11
- (void)_downloadImageWithCompletion:(void (^)(CGImageRef))finished
{
  _imageDownload = [_downloader downloadImageWithURL:_URL
                                       callbackQueue:dispatch_get_main_queue()
                               downloadProgressBlock:NULL
                                          completion:^(CGImageRef responseImage, NSError *error) {
                                            if (finished != NULL) {
                                              finished(responseImage);
                                            }
                                          }];
}

在给类NSURLRequest 利用runtime 添加属性,这里使用了static const char修饰符。 因为ASBasicImageDownloader 是 NSURLSession backed,所以只支持iOS7 以上的版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@interface NSURLRequest (ASBasicImageDownloader)
@property (nonatomic, strong) ASBasicImageDownloaderMetadata *asyncdisplaykit_metadata;
@end

@implementation NSURLRequest (ASBasicImageDownloader)
static const char *kMetadataKey = NSStringFromClass(ASBasicImageDownloaderMetadata.class).UTF8String;
- (void)setAsyncdisplaykit_metadata:(ASBasicImageDownloaderMetadata *)asyncdisplaykit_metadata
{
  objc_setAssociatedObject(self, kMetadataKey, asyncdisplaykit_metadata, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (ASBasicImageDownloader *)asyncdisplaykit_metadata
{
  return objc_getAssociatedObject(self, kMetadataKey);
}
@end

Cocoa Touch Develop Tips : Session1

Tips1: UILabel

设置UILabel 的一种比较时尚的方式, 运用了NSMutableParagraphStyle 和 NSAttributedString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor clearColor];
label.translatesAutoresizingMaskIntoConstraints = NO;
label.numberOfLines = 0;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
paragraphStyle.alignment = NSTextAlignmentCenter;
paragraphStyle.lineSpacing = 7;

NSDictionary *attributes = @{ NSFontAttributeName           : [UIFont ajkH3Font],
                              NSForegroundColorAttributeName: [UIColor brokerLightGrayColor],
                              NSParagraphStyleAttributeName : paragraphStyle
                            };
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:@"快去添加更多联系人,\n 邀请他们和你一起聊天吧!"
                                                                     attributes:attributes];
label.attributedText = attributedText;

[_addContactNotificationView addSubview:label];

[_addContactNotificationView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|"
                                                                                    options:0
                                                                                    metrics:nil
                                                                                      views:NSDictionaryOfVariableBindings(label)]];
[_addContactNotificationView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[label(50)]|"
                                                                                    options:0
                                                                                    metrics:nil
                                                                                      views:NSDictionaryOfVariableBindings(label)]];

Tips2: UIButton

UIButton有Property UIImageView 和 UILabel, 可以通过设置 imageEdgeInsets 和 titleEdgeInsets 来对这两者进行布局,这里要说明UIImageView 和 UILabel在 UIButton 中默认的位置布局如下:

|[UIImageView]-[UILabel]|

其中系统默认 [UIImageView]–[UILabel] 作为一个整体居中。 我们如何才能将其设置成以下布局

|-10-[UIImageView]-10-[UILabel]|

其中ImageView距离左边的padding 10.0f ,ImageView和Label的padding 也为10.0f
分别计算UIImageView和UILabel的宽度

1
2
3
4
5
6
7
8
CGFloat imageWith = image.size.width;
CGFloat titleWith = [button.titleLabel.attributedText size].width;

CGFloat imageInsetsLeft = - (button.width - imageWith - titleWith - padding * 2);    // negative 是向左偏里中心,其绝对值就是偏移的幅度。
CGFloat titleInsetLeft = imageInsetsLeft + padding;

button.imageEdgeInsets = UIEdgeInsetsMake(0, imageInsetsLeft, 0, 0);
button.titleEdgeInsets = UIEdgeInsetsMake(0, titleInsetLeft , 0, 0);

通过 imageEdgeInsets 和 titleEdgeInsets 控制image 和 label 的距离的关键,在于你要知晓,这两者默认的位置一起居中. 这样才能通过UIEdgeInsetsMake创建正确的edgeInsets。

这里顺便研究下 contentEdgeInsets 到底如何使用

Tips3: UITableViewCell

UITableViewCell 在被select 的之后,其背景色会变灰,而且其contentView 上面的所有subView 的backgroundColor 属性都会被设置成灰色。 假设UILabel 是contentView 的subView,那么改如何设计才能保证,其背景色在被选中的时候不会消失呢? 如果说,通过在UILabel 上调用 [label insertView:imageView atIndex:0] 但是不幸的是UILabel 上的文字会被覆盖掉。这样只能再引入一个UIView,但是这样会导致View 的hierachy 太复杂,影响性能。 其实有一个更为简单的办法:

1
2
label.backgroundColor = [UIColor clearColor];
label.layer.backgroundColor = [UIColor redColor].CGColor;

当在cell被selected之后,其label 的backgroundColor会被重新设置,但是layer上的backgroundColor不会改变。

LeetCode: Find Peak Element

A peak element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that num[-1] = num[n] = -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

Brute-force Solution

(find_peak_element.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
    int findPeakElement(const vector<int> &num) {
      int count = num.size();
      if (count == 1)
      {
          return 0;
      } else if (count > 1) {
          for (int i = 0; i < count; ++i){
              if (i >= 1 && i < count - 1 )
              {
                  if (num[i] > num[i-1] && num[i] > num[i+1])
                  {
                      return i;
                  }
              }
              if (i == 0)
              {
                  if (num[0] > num[1])
                  {
                      return 0;
                  }
              }
              if (i == count - 1)
              {
                  if (num[i] > num[i-1])
                  {
                      return count - 1;
                  }
                  }
         }
      }
    }
};

Time Complexity O(N) costs 40ms.

Divide & conquer Solution

(find_peak_element_dc.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    int findPeakElement(const vector<int> &num) {
int n = num.size();
    int low = 0;
    int high = n - 1;
    int mid = 0;

    while ( low < high ) {
        mid = low + ( high - low ) / 2;
        if ( ( mid == 0 || num[mid] > num[mid-1] ) &&
                ( mid == n-1 ||  num[mid] > num[mid+1] )  ){
            return mid;
        }

        if (mid >0 && num[mid-1] > num[mid]){
            high = mid - 1;
        }else{
            low = mid + 1;
        }
    }

    return low;
    }
};

Time Complexity O(logN) costs 16ms.

Math proof from here

LeetCode: Valid Parentheses

Given a string containing just the characters ‘(’, ‘)’, ‘{’, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.

The brackets must close in the correct order, “()” and “()[]{}” are all valid but “(]” and “([)]” are not.

Solution(in C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        for(int index = 0; index != s.size(); ++index){
            if (s[index] == '{' || s[index] == '[' || s[index] == '(') {
                st.push(s[index]);
            } else {
                if(st.size() == 0) return false;
                if (s[index] == '}' && st.top() != '{') {
                    return false;
                } else if(s[index] == ')' && st.top() != '('){
                    return false;
                } else if(s[index] == ']' && st.top() != '['){
                    return false;
                }
                st.pop();
            }
        }
        return st.empty() ? true: false;
    }
};

iOS Q&A Session 1

Problem1

为什么对类 A 的 delegate 属性设置成weak 或者assgin 会deadlock 。但是换成strong 可以正常运行。 self 本身也是另外一个类的局部变量。

1
2
3
4
5
6
7
- (void)createFilterBarFor:(AJKBSearchEstateViewController *)vc
{
  AJKBrokerAPI *brokerAPI = [[AJKBrokerAPI alloc] init];
  brokerAPI.delegate = self;
  [brokerAPI sendGetFilterWordsRequest];
  self.viewController = vc;
}

这时候delegate 所指的对象已经被释放掉了。所以运行到这里会出现 EXC_BAD_ACCESEX。像这类EXC_BAD_ACCESEX问题主要缘于access released objects

1
2
3
if ([self.delegate respondsToSelector:@selector(didLoadFilterItemsSuccess:)]) {
  [self.delegate didLoadFilterItemsSuccess:filtersArrays];
}

一个正确的做法是将A类的实例,设置成B的property,然后保证 delegate 的属性 设置成weak or assign这样避免环形引用。如果一定要将其创建成一个B的局部变量,那么就得将A的delegate 属性创建成Strong,这样才不至于出现EXC_BAD_ACCESS。

Problem2

为什么程序运行到

1
imageView.image = strongSelf.arrowImageViews[oppsiteIndex];

会crash 呢? 而且会不断 调用 imageView setImage 方法。是不是因为 迭代器里 block 执行时异步的?但是UI绘制时在主线程??

1
2
3
4
5
6
7
8
9
10
__weak typeof(self) weakSelf = self;
[self.arrowImageViews enumerateObjectsUsingBlock:^(UIImageView *imageView, NSUInteger idx, BOOL *stop) {
  __strong typeof(weakSelf) strongSelf = weakSelf;
  if (idx == index) {
    imageView.image = strongSelf.arrowImageViews[imageIndex];
  } else {
    NSInteger oppsiteIndex = imageIndex ^ 1;
    imageView.image = strongSelf.arrowImageViews[oppsiteIndex];
  }
}];

原因:低级错误,arrowImageViews (UIImageView 类型) 写错了,应该时arrowImags UIImage 类型 如果是UI代码已运行就crash,通常是因为传值类型不匹配导致的。

Problem3

在执行完之后,为什么self.emptyBackgroundView 还在其superView 中呢??

1
2
3
if (self.emptyBackgroundView) {
  [self.emptyBackgroundView removeFromSuperview];
}

但是通过po [self.view subviews] 发现有许多重复的 emptyBackgroundView view 加在了subview 数组里面,导致移除该 emptyBackgroundView ,还有很多 emptyBackgroundView。 所以看上去,好像是没有被remove掉。 出现这种情况是因为,[ self addSubView self.emptyBackground] api callBack 调用,多次请求之后会出现重复addSubview。这样view 的hierarchy重复的subview 会增加。

1
2
3
4
<AJKBSearchNoResultView: 0x7a622da0; frame = (0 46; 320 478); layer = <CALayer: 0x7a65c300>>,
<AJKBSearchNoResultView: 0x7a626c50; frame = (0 46; 320 478); layer = <CALayer: 0x7a6f4660>>,
<AJKBSearchNoResultView: 0x7ac688f0; frame = (0 46; 320 478); layer = <CALayer: 0x7ac67f30>>,
<AJKBSearchNoResultView: 0x7ac69cf0; frame = (0 46; 320 478); layer = <CALayer: 0x7ac69d60>>,

通过手段改善:

1
2
3
if (![self.view.subviews containsObject:self.emptyBackgroundView]){
  [self.view addSubview:self.emptyBackgroundView];
}
Problem4

对于subview 比较复杂的页面,如果要保证某个view可见 可以使用

1
[self.view bringSubviewToFront:self.tableView];

解决self.tableView 被遮挡的问题。

Problem5

如果是给UITablView 中delegate赋值A类的instance,一定得注意其UITableViewDelegate的方法和 UIScrollViewDelegate 方法只能在同一个类的instance中实现。

Problem6
1
2
3
4
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (self.navigationController.view.frame.origin.x > 0) {
  return;
}
Problem7

设置statusBar样式的方法

1
2
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
这是全局修改

设置navigationBar backgroundColor. iOS 7 和 iOS 6 有差异

1
2
3
4
5
6
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
  self.navigationController.navigationBar.barTintColor = NAVIGATIONBAR_COLOR;
  self.navigationController.navigationBar.translucent = YES;    
} else {
  self.navigationController.navigationBar.tintColor = NAVIGATIONBAR_COLOR;
}
Problem8

在TableView 试图回到最顶端,发现下面这个方法非常不稳定

1
[self.tableView setContentOffset:CGPointZero animated:YES];

换成下面这个方法

1
2
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
Problem9

UIViewController.view 其高度在 viewDidLoad 中ViewWillAppear 中是不同的。ViewWillAppear view 的高度已经去掉statusBar 和navigationBar 的高度。

LeetCode: Regular Expression Matching

Implement regular expression matching with support for ‘.’ and ‘*’. sequence is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

Solution

recurcive implementation:

(Regular_Expression_Matching.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
    bool isMatch(const char *s, const char *p) {
       assert(p && s);
       if ( *p = '\0') return *s == '\0';
       if (*(p+1) != '*') {
           assert(*p != '*');
           return ((*p == *s) || (*p == '.' && *s != '\0')) && isMatch(s+1, p+1);
       }
       while ((*p == *s) || (*p == '.' && *s != '\0')) {
            if (isMatch(s, p+2)) return true;
            s++;
     }
     return isMatch(s, p+2);
};

LeetCode: Gray Code

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

1
2
3
4
00 - 0
01 - 1
11 - 3
10 - 2

Note:

For a given n, a gray code sequence is not uniquely defined.

For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

Solution

(grayCode.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    vector<int> grayCode(int n) {
        std::vector<int> result;
            result.push_back(0);
            for (int i = 0; i < n; ++i)
            {
                int adder = 1 << i;
                int len = result.size();
                for (int i = len -1 ; i >= 0; --i)
                {
                    int element = result[i] + adder;
                    result.push_back(element);
                }
            }
        return result;
    }
};

LeetCode: 4Sum

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

  • Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
  • The solution set must not contain duplicate quadruplets.

For example, given array S = {1 0 -1 0 -2 2}, and target = 0. A solution set is:

1
2
3
4
5
(-1,  0, 0, 1)

(-2, -1, 1, 2)

(-2,  0, 0, 2)

Solution:

implememted in Java

(4sum.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Solution {
public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
    Arrays.sort(num);

    HashSet<ArrayList<Integer>> hashSet = new HashSet<ArrayList<Integer>>();
    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();

    for (int i = 0; i < num.length; i++) {
        for (int j = i + 1; j < num.length; j++) {
            int k = j + 1;
            int l = num.length - 1;

            while (k < l) {
                int sum = num[i] + num[j] + num[k] + num[l];

                if (sum > target) {
                    l--;
                } else if (sum < target) {
                    k++;
                } else if (sum == target) {
                    ArrayList<Integer> temp = new ArrayList<Integer>();
                    temp.add(num[i]);
                    temp.add(num[j]);
                    temp.add(num[k]);
                    temp.add(num[l]);

                    if (!hashSet.contains(temp)) {
                        hashSet.add(temp);
                        result.add(temp);
                    }

                    k++;
                    l--;
                }
            }
        }
    }
    return result;
}
}

implememted in C++

(4sum.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public:
    vector<vector<int> > fourSum(vector<int> &num, int target) {
        // Note: The Solution object is instantiated only once and is reused by each test case.
        vector<int> tmp;
        vector<vector<int>> res;
        if(num.empty()) return res;
        sort(num.begin(), num.end());
        for(int i=0; i<num.size(); i++)
        {
            int cur = target - num[i];
            for(int j=i+1; j<num.size(); j++)
            {
                int temp = cur - num[j];
                int start = j+1, end = num.size()-1;
                while(start<end)
                {
                    if(num[start]+num[end]==temp)
                    {
                        tmp.push_back(num[i]);
                        tmp.push_back(num[j]);
                        tmp.push_back(num[start]);
                        tmp.push_back(num[end]);
                        res.push_back(tmp);
                        tmp.clear();
                        start++;
                        end--;
                        while(start<end&&num[start]==num[start-1]) start++;
                        while(start<end&&num[end]==num[end+1]) end--;
                    }
                    else if(num[start]+num[end]<temp)
                    {
                        start++;
                        while(start<end&&num[start]==num[start-1]) start++;
                    }
                    else
                    {
                        end--;
                        while(start<end&&num[end]==num[end+1]) end--;
                    }
                }
                while(j<num.size()&&num[j]==num[j+1]) j++;
            }
            while(i<num.size()&&num[i]==num[i+1]) i++;
        }
        return res;
    }
};

Yi’s CV

Yi Qi(祁溢) |Born: 1989/02 | Location: Shanghai | Email:vitasone(AT)gmail.com

Education

  • 2011/09-2014/04 East China University of Science & Technology | Major: EE | Master
  • 2007/09-2011/06 Jiangxi University of Science & Technology | Major: EE | Bachelor

Work Experience

  • Anjuke Inc. iOS Developer Shanghai (2014/04 — present)
  • Anjuke Inc. iOS Developer Intern Shanghai (2013/12 — 2014/04)
  • Cisco System (China) R&D Center. Client and Mobility Business Unit Software Engineer Intern Shanghai (2012/07 — 2013/10)

Project Experience

Skill Set

Objective – C, C, Swift, Java, Python, Matlab

Publication

The effect of inhibitory neuron on the evolution model of higher-order coupling neural oscillator population. Computational and Mathematic Method in Medicine(SCI) 2013