引起UITableView卡顿比较常见的原因有cell的层级过多、cell中有触发离屏渲染的代码(譬如:cornerRadius、maskToBounds 同时使用)、像素是否对齐、是否使用UITableView自动计算cell高度的方法等。本文将从cell层级出发,以一个仿朋友圈的demo来讲述如何让列表保持顺滑,项目的源码可在文末获得。不可否认的是,过早的优化是魔鬼,请在项目出现性能瓶颈再考虑优化。

首先看看reveal上页面层级的效果图

然后是9.3系统iPhone5的真机效果

1、绘制文本

使用core text可以将文本绘制在一个CGContextRef上,最后再通过UIGraphicsGetImageFromCurrentImageContext()生成图片,再将图片赋值给cell.contentView.layer,从而达到减少cell层级的目的。

绘制普通文本(譬如用户昵称)在context上,相关注释在代码里:

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
- (void)drawInContext:(CGContextRef)context withPosition:(CGPoint)p andFont:(UIFont *)font andTextColor:(UIColor *)color andHeight:(float)height andWidth:(float)width lineBreakMode:(CTLineBreakMode)lineBreakMode {
    CGSize size = CGSizeMake(width, height);
    // 翻转坐标系
    CGContextSetTextMatrix(context,CGAffineTransformIdentity);
    CGContextTranslateCTM(context,0,height);
    CGContextScaleCTM(context,1.0,-1.0);
    NSMutableDictionary * attributes = [StringAttributes attributeFont:font andTextColor:color lineBreakMode:lineBreakMode];
    // 创建绘制区域(路径)
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path,NULL,CGRectMake(p.x, height-p.y-size.height,(size.width),(size.height)));
    // 创建AttributedString
    NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:self attributes:attributes];
    CFAttributedStringRef attributedString = (__bridge CFAttributedStringRef)attributedStr;
    // 绘制frame
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CTFrameRef ctframe = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0),path,NULL);
    CTFrameDraw(ctframe,context);
    CGPathRelease(path);
    CFRelease(framesetter);
    CFRelease(ctframe);
    [[attributedStr mutableString] setString:@""];
    CGContextSetTextMatrix(context,CGAffineTransformIdentity);
    CGContextTranslateCTM(context,0, height);
    CGContextScaleCTM(context,1.0,-1.0);
}

绘制朋友圈内容文本(带链接)在context上,这里我还没有去实现文本多了会折叠的效果,与上面普通文本不同的是这里需要创建带链接的AttributeString和CTLineRef的逐行绘制:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
- (NSMutableAttributedString *)highlightText:(NSMutableAttributedString *)coloredString{
    // 创建带高亮的AttributedString
    NSString* string = coloredString.string;
    NSRange range = NSMakeRange(0,[string length]);
    NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
    NSArray *matches = [linkDetector matchesInString:string options:0 range:range];
    for(NSTextCheckingResult* match in matches) {
        [self.ranges addObject:NSStringFromRange(match.range)];
        UIColor *highlightColor = UIColorFromRGB(0x297bc1);
        [coloredString addAttribute:(NSString*)kCTForegroundColorAttributeName
                              value:(id)highlightColor.CGColor range:match.range];
    }
    return coloredString;
}
- (void)drawFramesetter:(CTFramesetterRef)framesetter
       attributedString:(NSAttributedString *)attributedString
              textRange:(CFRange)textRange
                 inRect:(CGRect)rect
                context:(CGContextRef)c {
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL);
    CGFloat ContentHeight = CGRectGetHeight(rect);
    CFArrayRef lines = CTFrameGetLines(frame);
    NSInteger numberOfLines = CFArrayGetCount(lines);
    CGPoint lineOrigins[numberOfLines];
    CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);
    // 遍历每一行
    for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
        CGPoint lineOrigin = lineOrigins[lineIndex];
        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);
        CGFloat descent = 0.0f, ascent = 0.0f, lineLeading = 0.0f;
        CTLineGetTypographicBounds((CTLineRef)line, &ascent, &descent, &lineLeading);
        CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, NSTextAlignmentLeft, rect.size.width);
        CGFloat y = lineOrigin.y - descent - self.font.descender;
        // 设置每一行位置
        CGContextSetTextPosition(c, penOffset + self.xOffset, y - self.yOffset);
        CTLineDraw(line, c);
        // CTRunRef同一行中文本的不同样式,包括颜色、字体等,此处用途为处理链接高亮
        CFArrayRef runs = CTLineGetGlyphRuns(line);
        for (int j = 0; j < CFArrayGetCount(runs); j++) {
            CGFloat runAscent, runDescent, lineLeading1;
            CTRunRef run = CFArrayGetValueAtIndex(runs, j);
            NSDictionary *attributes = (__bridge NSDictionary*)CTRunGetAttributes(run);
            // 判断是不是链接
            if (!CGColorEqualToColor((__bridge CGColorRef)([attributes valueForKey:@"CTForegroundColor"]), self.textColor.CGColor)) {
                CFRange range = CTRunGetStringRange(run);
                float offset = CTLineGetOffsetForStringIndex(line, range.location, NULL);
                // 得到链接的CGRect
                CGRect runRect;
                runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, &lineLeading1);
                runRect.size.height = self.font.lineHeight;
                runRect.origin.x = lineOrigin.x + offset+ self.xOffset;
                runRect.origin.y = lineOrigin.y;
                runRect.origin.y -= descent + self.yOffset;
                // 因为坐标系被翻转,链接正常的坐标需要通过CGAffineTransform计算得到
                CGAffineTransform transform = CGAffineTransformMakeTranslation(0, ContentHeight);
                transform = CGAffineTransformScale(transform, 1.f, -1.f);
                CGRect flipRect = CGRectApplyAffineTransform(runRect, transform);
                // 保存是链接的CGRect
                NSRange nRange = NSMakeRange(range.location, range.length);
                self.framesDict[NSStringFromRange(nRange)] = [NSValue valueWithCGRect:flipRect];
                // 保存同一条链接的不同CGRect,用于点击时背景色处理
                for (NSString *rangeString in self.ranges) {
                    NSRange range = NSRangeFromString(rangeString);
                    if (NSLocationInRange(nRange.location, range)) {
                        NSMutableArray *array = self.relationDict[rangeString];
                        if (array) {
                            [array addObject:NSStringFromCGRect(flipRect)];
                            self.relationDict[rangeString] = array;
                        else {
                            self.relationDict[rangeString] = [NSMutableArray arrayWithObject:NSStringFromCGRect(flipRect)];
                        }
                    }
                }
            }
        }
    }
    CFRelease(frame);
    CFRelease(path);
}

上述方法运用起来就是:

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
- (void)fillData:(CGContextRef)context {
    [self.nickname drawInContext:context withPosition:(CGPoint){kTextXOffset, kSpec} andFont:kNicknameFont
                    andTextColor:UIColorFromRGB(0x556c95) andHeight:self.nicknameSize.height
                        andWidth:self.nicknameSize.width lineBreakMode:kCTLineBreakByTruncatingTail];
    [self.drawer setText:self.contentString context:context contentSize:self.contentSize
         backgroundColor:[UIColor whiteColor] font:kContentTextFont textColor:[UIColor blackColor]
                   block:nil xOffset:kTextXOffset yOffset:kSpec * 2 + self.nicknameSize.height];
}
- (void)fillContents:(NSArray *)array {
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.size.width, self.size.height), YES, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [UIColorFromRGB(0xffffff) set];
    CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
    // 获取需要高亮的链接CGRect,并填充背景色
    if (array) {
        for (NSString *string in array) {
            CGRect rect = CGRectFromString(string);
            [UIColorFromRGB(0xe5e5e5) set];
            CGContextFillRect(context, rect);
        }
    }
    [self fillData:context];
    UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.contentView.layer.contents = (__bridge id _Nullable)(temp.CGImage);
}

这样就完成了文本的显示。

2、显示图片

图片包括用户头像和朋友圈的内容,这里只是将CALayer添加到contentView.layer上,具体做法是继承了CALayer,实现部分功能。

通过链接显示图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)setContentsWithURLString:(NSString *)urlString {
    self.contents = (__bridge id _Nullable)([UIImage imageNamed:@"placeholder"].CGImage);
    @weakify(self)
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager downloadImageWithURL:[NSURL URLWithString:urlString]
                          options:SDWebImageCacheMemoryOnly
                         progress:nil
                        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                            if (image) {
                                @strongify(self)
                                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                                    if (!_observer) {
                                        _observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopExit, false, POPAnimationApplyRunLoopOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
                                            self.contents = (__bridge id _Nullable)(image.CGImage);
                                        });
                                        if (_observer) {
                                            CFRunLoopAddObserver(CFRunLoopGetMain(), _observer,  kCFRunLoopCommonModes);
                                        }
                                    }
                                });
                                self.originImage = image;
                            }
                        }];
}

其他比较简单就不展开。

3、显示小视频

之前的一篇文章简单讲了怎么自己做一个播放器,这里就派上用场了。而显示小视频封面图片的CALayer同样在显示小视频的时候可以复用。

这里使用了NSOperationQueue来保障播放视频的流畅性,具体继承NSOperation的VideoDecodeOperation相关代码如下:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
- (void)main {
    @autoreleasepool {
        if (self.isCancelled) {
            _newVideoFrameBlock = nil;
            _decodeFinishedBlock = nil;
            return;
        }
        AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:self.filePath] options:nil];
        NSError *error;
        AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
        if (error) {
            return;
        }
        NSArray* videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
        AVAssetTrack* videoTrack = [videoTracks objectAtIndex:0];
        // 视频播放时,m_pixelFormatType=kCVPixelFormatType_32BGRA
        // 其他用途,如视频压缩,m_pixelFormatType=kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
        int m_pixelFormatType = kCVPixelFormatType_32BGRA;
        NSDictionary* options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt: (int)m_pixelFormatType]
                                                            forKey:(id)kCVPixelBufferPixelFormatTypeKey];
        AVAssetReaderTrackOutput* videoReaderOutput = [[AVAssetReaderTrackOutput alloc]
                initWithTrack:videoTrack outputSettings:options];
        [reader addOutput:videoReaderOutput];
        [reader startReading];
        // 要确保nominalFrameRate>0,之前出现过android拍的0帧视频
        if (self.isCancelled) {
            _newVideoFrameBlock = nil;
            _decodeFinishedBlock = nil;
            return;
        }
        while ([reader status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate > 0) {
            if (self.isCancelled) {
                _newVideoFrameBlock = nil;
                _decodeFinishedBlock = nil;
                return;
            }
            CMSampleBufferRef sampleBuffer = [videoReaderOutput copyNextSampleBuffer];
            CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
            // Lock the base address of the pixel buffer
            CVPixelBufferLockBaseAddress(imageBuffer, 0);
            // Get the number of bytes per row for the pixel buffer
            size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
            // Get the pixel buffer width and height
            size_t width = CVPixelBufferGetWidth(imageBuffer);
            size_t height = CVPixelBufferGetHeight(imageBuffer);
            //Generate image to edit`
            unsigned char* pixel = (unsigned char *)CVPixelBufferGetBaseAddress(imageBuffer);
            CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
            CGContextRef context=CGBitmapContextCreate(pixel, width, height, 8, bytesPerRow, colorSpace,
                                                       kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
            if (context != NULL) {
                CGImageRef imageRef = CGBitmapContextCreateImage(context);
                CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
                CGColorSpaceRelease(colorSpace);
                CGContextRelease(context);
                // 解码图片
                size_t width = CGImageGetWidth(imageRef);
                size_t height = CGImageGetHeight(imageRef);
                size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
                // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate
                size_t bytesPerRow = 0;
                CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
                CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
                CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
                if (colorSpaceModel == kCGColorSpaceModelRGB) {
                    uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wassign-enum"
                    if (alpha == kCGImageAlphaNone) {
                        bitmapInfo &= ~kCGBitmapAlphaInfoMask;
                        bitmapInfo |= kCGImageAlphaNoneSkipFirst;
                    else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
                        bitmapInfo &= ~kCGBitmapAlphaInfoMask;
                        bitmapInfo |= kCGImageAlphaPremultipliedFirst;
                    }
#pragma clang diagnostic pop
                }
                CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent,
                                                             bytesPerRow, colorSpace, bitmapInfo);
                CGColorSpaceRelease(colorSpace);
                if (!context) {
                    if (self.newVideoFrameBlock) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            if (self.isCancelled) {
                                _newVideoFrameBlock = nil;
                                _decodeFinishedBlock = nil;
                                return;
                            }
                            self.newVideoFrameBlock(imageRef, self.filePath);
                            CGImageRelease(imageRef);
                        });
                    }
                else {
                    CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);
                    CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
                    CGContextRelease(context);
                    if (self.newVideoFrameBlock) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            if (self.isCancelled) {
                                _newVideoFrameBlock = nil;
                                _decodeFinishedBlock = nil;
                                return;
                            }
                            self.newVideoFrameBlock(inflatedImageRef, self.filePath);
                            CGImageRelease(inflatedImageRef);
                        });
                    }
                    CGImageRelease(imageRef);
                }
                if(sampleBuffer) {
                    CMSampleBufferInvalidate(sampleBuffer);
                    CFRelease(sampleBuffer);
                    sampleBuffer = NULL;
                else {
                    break;
                }
            }
            [NSThread sleepForTimeInterval:CMTimeGetSeconds(videoTrack.minFrameDuration)];
        }
        if (self.isCancelled) {
            _newVideoFrameBlock = nil;
            _decodeFinishedBlock = nil;
            return;
        }
        if (self.decodeFinishedBlock) {
            self.decodeFinishedBlock(self.filePath);
        }
    }
}

解码图片是因为UIImage在界面需要显示的时候才开始解码,这样可能会造成主线程的卡顿,所以在子线程对其进行解压缩处理。

具体的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)playVideoWithFilePath:(NSString *)filePath_ type:(NSString *)type {
    @weakify(self)
    [[VideoPlayerManager shareInstance] decodeVideo:filePath_
                              withVideoPerDataBlock:^(CGImageRef imageData, NSString *filePath) {
                                  @strongify(self)
                                  if ([type isEqualToString:@"video"]) {
                                      if ([filePath isEqualToString:self.filePath]) {
                                          [self.sources.firstObject
                                                  setContents:(__bridge id _Nullable)(imageData)];
                                      }
                                  }
                              } decodeFinishBlock:^(NSString *filePath){
                [self playVideoWithFilePath:filePath type:type];
            }];
}

4、其他

1、触摸交互是覆盖了以下方法实现:

1
2
3
- (void)touchesCancelled:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event</uitouch *></uitouch *></uitouch *>

2、页面上FPS的测量是使用了YYKit项目中的YYFPSLabel。

3、测试数据是微博找的,其中小视频是Gif快手。

本文的代码在https://github.com/hawk0620/PYQFeedDemo

本文作者:伯乐在线 - Hawk0620

【转】基于 CoreText 实现的高性能 UITableView的更多相关文章

  1. 基于 CoreText 实现的高性能 UITableView

    引起UITableView卡顿比较常见的原因有cell的层级过多.cell中有触发离屏渲染的代码(譬如:cornerRadius.maskToBounds 同时使用).像素是否对齐.是否使用UITab ...

  2. iOS:基于CoreText的排版引擎

    一.CoreText的简介 CoreText是用于处理文字和字体的底层技术.它直接和Core Graphics(又被称为Quartz)打交道.Quartz是一个2D图形渲染引擎,能够处理OSX和iOS ...

  3. 基于.NET MVC的高性能IOC插件化架构

    基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博 ...

  4. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  5. 基于CoreText的排版引擎

    前言 本人今年主要在负责猿题库iOS客户端的开发,本文旨在通过分享猿题库iOS客户端开发过程中的技术细节,达到总结和交流的目的. 这是本技术分享系列文章的第三篇.本文涉及的技术细节是:基于CoreTe ...

  6. 基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc

    基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc 二月 8, 2016 1 简介 Navi-pbrpc框架是一个高性能的远程调用RPC框架,使用netty4技术提供非阻塞.异步.全 ...

  7. 基于 CoreText 实现高性能 UITableView

    引起UITableView卡顿比较常见的原因有cell的层级过多.cell中有触发离屏渲染的代码(譬如:cornerRadius.maskToBounds 同时使用).像素是否对齐.是否使用UITab ...

  8. 基于MINA构建简单高性能的NIO应用

    mina是非常好的C/S架构的java服务器,这里转了一篇关于它的使用感受. 前言MINA是Trustin Lee最新制作的Java通讯框架.通讯框架的主要作用是封装底层IO操作,提供高级的操作API ...

  9. 基于开源软件构建高性能集群NAS系统,包括负载均衡(刘爱贵)

    大数据时代的到来已经不可阻挡,面对数据的爆炸式增长,尤其是半结构化数据和非结构化数据,NoSQL存储系统和分布式文件系统成为了技术浪潮,得到了长足的发展.非结构化数据目前呈现更加快速的增长趋势,IDC ...

随机推荐

  1. 【转载】Android内存泄露

    相信一步步走过来的Android从业者,每个人都会遇到OOM的情况.如何避免和防范OOM的出现,对于每一个程序员来说确实是一门必不可少的能力.今天我们就谈谈在Android平台下内存的管理之道,开始今 ...

  2. linux 查看机器的cpu,操作系统等命令

    看cpu信息,型号,几核 [root@f3 ~]# cat /proc/cpuinfo | grep name | cut -f2 -d:| uniq -c 16 Intel(R) Xeon(R) C ...

  3. BZOJ4699 : 树上的最短路

    这道题主要是要解决以下两个问题: 问题1: 给定一个点$x$,如何取出所有经过它的下水道? 一条下水道经过$x$等价于它起点在$x$的子树里面且终点不在$x$的子树里面,或者两端点的lca就是$x$. ...

  4. 在SUBLIME TEXT中安装SUBLIMELINTER进行JS&CSS代码校验

    一:Sublime Text 中需要先安装Package Control.(如果有则无需安装) 安装方法:打开Sublime Text控制台(快捷键Ctrl+`),在控制台粘贴以下代码,按回车执行. ...

  5. CSS样式覆盖顺序

    有时候在写CSS的过程中,某些限制总是不起作用,这就涉及了CSS样式覆盖的问题,如下 Css代码   #navigator { height: 100%; width: 200; position:  ...

  6. 将类似 12.56MB 36.89KB 转成 以K为单位的数字【备忘】

    select case RIGHT(RESOURCE_SIZE,2) when 'MB' THEN SUBSTRING_INDEX(RESOURCE_SIZE,'MB',1)*1024 ELSE SU ...

  7. NOI模拟赛Day5

    T1 有and,xor,or三种操作,每个人手中一个数,求和左边进行某一种运算的最大值,当t==2时,还需要求最大值的个数. test1 20% n<=1000 O(n^2)暴力 test2 2 ...

  8. BZOJ4553: [Tjoi2016&Heoi2016]序列

    Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值 可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所 ...

  9. Bouncy Castle内存溢出

    现象: 堆内存溢出,java.lang.OutOfMemoryError: Java heap space 用jmap查看,显示 num     #instances         #bytes   ...

  10. 纪念逝去的岁月——C/C++排序二叉树

    1.代码 2.运行结果 3.分析 1.代码 #include <stdio.h> #include <stdlib.h> typedef struct _Node { int ...