当要读写内存中某个字节时,首先检查这个字节对应的 A bit。如果该A bit显示该位置是无效位置,memcheck 则报告读写错误。
内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。
==2989== Memcheck, a memory error detector ==2989== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==2989== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==2989== Command: ./test ==2989== ==2989== Invalid write of size 4 ==2989== at 0x4004E2: k (test.c:5) ==2989== by 0x4004F2: main (test.c:10) ==2989== Address 0x4c27064 is 4 bytes after a block of size 32 alloc'd ==2989== at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==2989== by 0x4004D5: k (test.c:4) ==2989== by 0x4004F2: main (test.c:10) ==2989== ==2989== ==2989== HEAP SUMMARY: ==2989== in use at exit: 32 bytes in 1 blocks ==2989== total heap usage: 1 allocs, 0 frees, 32 bytes allocated ==2989== ==2989== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2989== at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==2989== by 0x4004D5: k (test.c:4) ==2989== by 0x4004F2: main (test.c:10) ==2989== ==2989== LEAK SUMMARY: ==2989== definitely lost: 32 bytes in 1 blocks ==2989== indirectly lost: 0 bytes in 0 blocks ==2989== possibly lost: 0 bytes in 0 blocks ==2989== still reachable: 0 bytes in 0 blocks ==2989==suppressed: 0 bytes in 0 blocks ==2989== ==2989== For counts of detected and suppressed errors, rerun with: -v ==2989== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)
==3058== Memcheck, a memory error detector ==3058== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==3058== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==3058== Command: ./t2 ==3058==
[a] ==3058== Invalid read of size 1 ==3058== at 0x4005A3: main (t2.c:14) ==3058== Address 0x4c27040 is 0 bytes inside a block of size 1 free'd ==3058== at 0x4A06430: free (vg_replace_malloc.c:446) ==3058== by 0x40059E: main (t2.c:13) ==3058== ==3058== ==3058== HEAP SUMMARY: ==3058== in use at exit: 0 bytes in 0 blocks ==3058== total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==3058== ==3058== All heap blocks were freed -- no leaks are possible ==3058== ==3058== For counts of detected and suppressed errors, rerun with: -v ==3058== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
从上输出内容可以看到,Valgrind检测到无效的读取操作然后输出“Invalid read of size 1”。
==3128== Memcheck, a memory error detector ==3128== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==3128== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==3128== Command: ./t3 ==3128== ==3128== Invalid read of size 1 #无效读取 ==3128==at 0x400579: main (t3.c:9) ==3128==Address 0x4c27041 is 0 bytes after a block of size 1 alloc'd ==3128==at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==3128==by 0x400565: main (t3.c:6) ==3128==[] ==3128== ==3128== HEAP SUMMARY: ==3128==in use at exit: 0 bytes in 0 blocks ==3128==total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==3128== ==3128== All heap blocks were freed -- no leaks are possible ==3128== ==3128== For counts of detected and suppressed errors, rerun with: -v ==3128== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
内存泄露
1 2 3 4 5 6 7 8 9 10 11
#include<stdio.h> #include<stdlib.h>
intmain(void) { int *p = malloc(1); *p = 'x'; char c = *p; printf("%c\n",c); //申请后未释放 return0; }
==3221== Memcheck, a memory error detector ==3221== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==3221== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==3221== Command: ./t4 ==3221== ==3221== Invalid write of size 4 ==3221==at 0x40051E: main (t4.c:7) ==3221==Address 0x4c27040 is 0 bytes inside a block of size 1 alloc'd ==3221==at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==3221==by 0x400515: main (t4.c:6) ==3221== ==3221== Invalid read of size 4 ==3221==at 0x400528: main (t4.c:8) ==3221==Address 0x4c27040 is 0 bytes inside a block of size 1 alloc'd ==3221==at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==3221==by 0x400515: main (t4.c:6) ==3221== ==3221== ==3221== HEAP SUMMARY: ==3221==in use at exit: 1 bytes in 1 blocks ==3221==total heap usage: 1 allocs, 0 frees, 1 bytes allocated ==3221== ==3221== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==3221==at 0x4A06A2E: malloc (vg_replace_malloc.c:270) ==3221==by 0x400515: main (t4.c:6) ==3221== ==3221== LEAK SUMMARY: ==3221==definitely lost: 1 bytes in 1 blocks ==3221==indirectly lost: 0 bytes in 0 blocks ==3221== possibly lost: 0 bytes in 0 blocks ==3221==still reachable: 0 bytes in 0 blocks ==3221== suppressed: 0 bytes in 0 blocks ==3221== ==3221== For counts of detected and suppressed errors, rerun with: -v ==3221== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 6 from 6)
==3294== Memcheck, a memory error detector ==3294== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==3294== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==3294== Command: ./t5 ==3294== ==3294== Conditional jump or move depends on uninitialised value(s) ==3294== at 0x3CD4C47E2C: vfprintf (in /lib64/libc-2.12.so) ==3294== by 0x3CD4C4F189: printf (in /lib64/libc-2.12.so) ==3294== by 0x400589: main (t5.c:9) ==3294== ==3294== Invalid free() / delete / delete[] / realloc() ==3294== at 0x4A06430: free (vg_replace_malloc.c:446) ==3294== by 0x4005B5: main (t5.c:13) ==3294== Address 0x4c27040 is 0 bytes inside a block of size 100 free'd ==3294== at 0x4A06430: free (vg_replace_malloc.c:446) ==3294== by 0x4005A9: main (t5.c:12) ==3294== ==3294== Invalid free() / delete / delete[] / realloc() ==3294== at 0x4A06430: free (vg_replace_malloc.c:446) ==3294== by 0x4005C1: main (t5.c:14) ==3294== Address 0x4c27040 is 0 bytes inside a block of size 100 free'd ==3294== at 0x4A06430: free (vg_replace_malloc.c:446) ==3294== by 0x4005A9: main (t5.c:12) ==3294== Memory Allocated at: /n==3294== ==3294== HEAP SUMMARY: ==3294== in use at exit: 0 bytes in 0 blocks ==3294== total heap usage: 1 allocs, 3 frees, 100 bytes allocated
MASSIF OPTIONS --heap=<yes|no> [default: yes] Specifies whether heap profiling should be done.
--heap-admin=<size> [default: 8] If heap profiling is enabled, gives the number of administrative bytes per block to use. This should be an estimate of the average, since it may vary. For example, the allocator used by glibc on Linux requires somewhere between 4 to 15 bytes per block, depending on various factors. That allocator also requires admin space for freed blocks, but Massif cannot account for this.
--stacks=<yes|no> [default: no] Specifies whether stack profiling should be done. This option slows Massif down greatly, and so is off by default. Note that Massif assumes that the main stack has size zero at start-up. This is not true, but doing otherwise accurately is difficult. Furthermore, starting at zero better indicates the size of the part of the main stack that a user program actually has control over.
--pages-as-heap=<yes|no> [default: no] Tells Massif to profile memory at the page level rather than at the malloc'd block level. See above for details.
--depth=<number> [default: 30] Maximum depth of the allocation trees recorded for detailed snapshots. Increasing it will make Massif run somewhat more slowly, use more memory, and produce bigger output files.
--alloc-fn=<name> Functions specified with this option will be treated as though they were a heap allocation function such as malloc. This is useful for functions that are wrappers to malloc or new, which can fill up the allocation trees with uninteresting information. This option can be specified multiple times on the command line, to name multiple functions.
Note that the named function will only be treated this way if it is the top entry in a stack trace, or just below another function treated this way. For example, if you have a function malloc1 that wraps malloc, and malloc2 that wraps malloc1, just specifying --alloc-fn=malloc2 will have no effect. You need to specify --alloc-fn=malloc1 as well. This is a little inconvenient, but the reason is that checking for allocation functions is slow, and it saves a lot of time if Massif can stop looking through the stack trace entries as soon as it finds one that doesn't match rather than having to continue through all the entries.
Note that C++ names are demangled. Note also that overloaded C++ names must be written in full. Single quotes may be necessary to prevent the shell from breaking them up. For example:
--ignore-fn=<name> Any direct heap allocation (i.e. a call to malloc, new, etc, or a call to a function named by an --alloc-fn option) that occurs in a function specified by this option will be ignored. This is mostly useful for testing purposes. This option can be specified multiple times on the command line, to name multiple functions.
Any realloc of an ignored block will also be ignored, even if the realloc call does not occur in an ignored function. This avoids the possibility of negative heap sizes if ignored blocks are shrunk with realloc.
The rules for writing C++ function names are the same as for --alloc-fn above.
--threshold=<m.n> [default: 1.0] The significance threshold for heap allocations, as a percentage of total memory size. Allocation tree entries that account for less than this will be aggregated. Note that this should be specified in tandem with ms_print's option of the same name.
--peak-inaccuracy=<m.n> [default: 1.0] Massif does not necessarily record the actual global memory allocation peak; by default it records a peak only when the global memory allocation size exceeds the previous peak by at least 1.0%. This is because there can be many local allocation peaks along the way, and doing a detailed snapshot for every one would be expensive and wasteful, as all but one of them will be later discarded. This inaccuracy can be changed (even to 0.0%) via this option, but Massif will run drastically slower as the number approaches zero.
--time-unit=<i|ms|B> [default: i] The time unit used for the profiling. There are three possibilities: instructions executed (i), which is good for most cases; real (wallclock) time (ms, i.e. milliseconds), which is sometimes useful; and bytes allocated/deallocated on the heap and/or stack (B), which is useful for very short-run programs, and for testing purposes, because it is the most reproducible across different machines.
--detailed-freq=<n> [default: 10] Frequency of detailed snapshots. With --detailed-freq=1, every snapshot is detailed.
--max-snapshots=<n> [default: 100] The maximum number of snapshots recorded. If set to N, for all programs except very short-running ones, the final number of snapshots will be between N/2 and N.
--massif-out-file=<file> [default: massif.out.%p] Write the profile data to file rather than to the default output file, massif.out.<pid>. The %p and %q format specifiers can be used to embed the process ID and/or the contents of an environment variable in the name, as is the case for the core option --log-file.
不幸的是,在glibc的一些版本中,libc_freeres是有bug会导致段错误的。这在Red Hat 7.1上有特别声明。所以,提供这个选项来决定是否运行libc_freeres。如果你的程序看起来在Valgrind上运行得很好,但是在退出时发生段错误,你可能需要指定—run-libc-freeres=no来修正,这将可能错误的报告libc.so的内存泄漏。
Enable AddressSanitizer, a fast memory error detector. Memory access instructions are instrumented to detect out-of-bounds and use-after-free bugs. The option enables -fsanitize-address-use-after-scope. See https://github.com/google/sanitizers/wiki/AddressSanitizer for more details. The run-time behavior can be influenced using the ASAN_OPTIONS environment variable. When set to help=1, the available options are shown at startup of the instrumented program. See https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags for a list of supported options. The option cannot be combined with -fsanitize=thread and/or -fcheck-pointer-bounds.
-fsanitize=kernel-address: Enable AddressSanitizer for Linux kernel. See https://github.com/google/kasan/wiki for more details. The option cannot be combined with -fcheck-pointer-bounds.
-fsanitize=thread: Enable ThreadSanitizer, a fast data race detector. Memory access instructions are instrumented to detect data race bugs. See https://github.com/google/sanitizers/wiki#threadsanitizer for more details. The run-time behavior can be influenced using the TSAN_OPTIONS environment variable; see https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags for a list of supported options. The option cannot be combined with -fsanitize=address, -fsanitize=leak and/or -fcheck-pointer-bounds.
Note that sanitized atomic builtins cannot throw exceptions when operating on invalid memory addresses with non-call exceptions (-fnon-call-exceptions).
-fsanitize=leak: Enable LeakSanitizer, a memory leak detector. This option only matters for linking of executables and the executable is linked against a library that overrides malloc and other allocator functions. See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer for more details. The run-time behavior can be influenced using the LSAN_OPTIONS environment variable. The option cannot be combined with -fsanitize=thread.
-fsanitize=undefined: Enable UndefinedBehaviorSanitizer, a fast undefined behavior detector. Various computations are instrumented to detect undefined behavior at runtime. Current suboptions are:
-fsanitize=shift: This option enables checking that the result of a shift operation is not undefined. Note that what exactly is considered undefined differs slightly between C and C++, as well as between ISO C90 and C99, etc. This option has two suboptions, -fsanitize=shift-base and -fsanitize=shift-exponent.
-fsanitize=shift-exponent: This option enables checking that the second argument of a shift operation is not negative and is smaller than the precision of the promoted first argument.
-fsanitize=shift-base: If the second argument of a shift operation is within range, check that the result of a shift operation is not undefined. Note that what exactly is considered undefined differs slightly between C and C++, as well as between ISO C90 and C99, etc.
-fsanitize=integer-divide-by-zero: Detect integer division by zero as well as INT_MIN / -1 division.
-fsanitize=unreachable: With this option, the compiler turns the builtin_unreachable call into a diagnostics message call instead. When reaching the builtin_unreachable call, the behavior is undefined.
-fsanitize=vla-bound: This option instructs the compiler to check that the size of a variable length array is positive.
-fsanitize=null: This option enables pointer checking. Particularly, the application built with this option turned on will issue an error message when it tries to dereference a NULL pointer, or if a reference (possibly an rvalue reference) is bound to a NULL pointer, or if a method is invoked on an object pointed by a NULL pointer.
-fsanitize=return: This option enables return statement checking. Programs built with this option turned on will issue an error message when the end of a non-void function is reached without actually returning a value. This option works in C++ only.
-fsanitize=signed-integer-overflow: This option enables signed integer overflow checking. We check that the result of +, *, and both unary and binary – does not overflow in the signed arithmetics. Note, integer promotion rules must be taken into account. That is, the following is not an overflow:
1 2
signedchar a = SCHAR_MAX; a++;
-fsanitize=bounds: This option enables instrumentation of array bounds. Various out of bounds accesses are detected. Flexible array members, flexible array member-like arrays, and initializers of variables with static storage are not instrumented. The option cannot be combined with -fcheck-pointer-bounds.
-fsanitize=bounds-strict: This option enables strict instrumentation of array bounds. Most out of bounds accesses are detected, including flexible array members and flexible array member-like arrays. Initializers of variables with static storage are not instrumented. The option cannot be combined with -fcheck-pointer-bounds.
-fsanitize=alignment: This option enables checking of alignment of pointers when they are dereferenced, or when a reference is bound to insufficiently aligned target, or when a method or constructor is invoked on insufficiently aligned object.
-fsanitize=object-size: This option enables instrumentation of memory references using the __builtin_object_size function. Various out of bounds pointer accesses are detected.
-fsanitize=float-divide-by-zero: Detect floating-point division by zero. Unlike other similar options, -fsanitize=float-divide-by-zero is not enabled by -fsanitize=undefined, since floating-point division by zero can be a legitimate way of obtaining infinities and NaNs.
-fsanitize=float-cast-overflow: This option enables floating-point type to integer conversion checking. We check that the result of the conversion does not overflow. Unlike other similar options, -fsanitize=float-cast-overflow is not enabled by -fsanitize=undefined. This option does not work well with FE_INVALID exceptions enabled.
-fsanitize=nonnull-attribute: This option enables instrumentation of calls, checking whether null values are not passed to arguments marked as requiring a non-null value by the nonnull function attribute.
-fsanitize=returns-nonnull-attribute: This option enables instrumentation of return statements in functions marked with returns_nonnull function attribute, to detect returning of null values from such functions.
-fsanitize=bool: This option enables instrumentation of loads from bool. If a value other than 0/1 is loaded, a run-time error is issued.
-fsanitize=enum: This option enables instrumentation of loads from an enum type. If a value outside the range of values for the enum type is loaded, a run-time error is issued.
-fsanitize=vptr: This option enables instrumentation of C++ member function calls, member accesses and some conversions between pointers to base and derived classes, to verify the referenced object has the correct dynamic type.
While -ftrapv causes traps for signed overflows to be emitted, -fsanitize=undefined gives a diagnostic message. This currently works only for the C family of languages.
-fno-sanitize=all: This option disables all previously enabled sanitizers. -fsanitize=all is not allowed, as some sanitizers cannot be used together.
-fasan-shadow-offset=number: This option forces GCC to use custom shadow offset in AddressSanitizer checks. It is useful for experimenting with different shadow memory layouts in Kernel AddressSanitizer.
-fsanitize-sections=s1,s2,…: Sanitize global variables in selected user-defined sections. si may contain wildcards.
-fsanitize-recover[=opts]: -fsanitize-recover= controls error recovery mode for sanitizers mentioned in comma-separated list of opts. Enabling this option for a sanitizer component causes it to attempt to continue running the program as if no error happened. This means multiple runtime errors can be reported in a single program run, and the exit code of the program may indicate success even when errors have been reported. The -fno-sanitize-recover= option can be used to alter this behavior: only the first detected error is reported and program then exits with a non-zero exit code.
Currently this feature only works for -fsanitize=undefined (and its suboptions except for -fsanitize=unreachable and -fsanitize=return), -fsanitize=float-cast-overflow, -fsanitize=float-divide-by-zero, -fsanitize=bounds-strict, -fsanitize=kernel-address and -fsanitize=address. For these sanitizers error recovery is turned on by default, except -fsanitize=address, for which this feature is experimental. -fsanitize-recover=all and -fno-sanitize-recover=all is also accepted, the former enables recovery for all sanitizers that support it, the latter disables recovery for all sanitizers that support it.
Even if a recovery mode is turned on the compiler side, it needs to be also enabled on the runtime library side, otherwise the failures are still fatal. The runtime library defaults to halt_on_error=0 for ThreadSanitizer and UndefinedBehaviorSanitizer, while default value for AddressSanitizer is halt_on_error=1. This can be overridden through setting the halt_on_error flag in the corresponding environment variable.
Syntax without an explicit opts parameter is deprecated. It is equivalent to specifying an opts list of:
-fsanitize-address-use-after-scope: Enable sanitization of local variables to detect use-after-scope bugs. The option sets -fstack-reuse to ‘none’.
-fsanitize-undefined-trap-on-error: The -fsanitize-undefined-trap-on-error option instructs the compiler to report undefined behavior using __builtin_trap rather than a libubsan library routine. The advantage of this is that the libubsan library is not needed and is not linked in, so this is usable even in freestanding environments.
-fsanitize-coverage=trace-pc: Enable coverage-guided fuzzing code instrumentation. Inserts a call to __sanitizer_cov_trace_pc into every basic block
当内核控制路径必须访问共享数据结构或进入临界区时,就需要为自己获取一把“锁”。自旋锁时用来在多处理器环境中工作的一种特殊的锁。如果内核控制路径发现锁被运行在另一个 CPU 上的内核控制路径“锁着”,就会在周围“旋转”,反复执行一条紧凑的循环指令(“忙等”),直到锁被释放。自旋锁的循环指令表示忙等,即使等待的CPU无事可做也会占用CPU。
宏local_bh_disable给本地 CPU 的软中断计数器加 1。local_bh_enable()函数从本地 CPU 的软中断计数器中减 1。内核因此能使用几个嵌套的local_bh_disable调用,只有宏local_bj_enable与第一个local_bh_disable调用相匹配,可延迟函数才再次被激活。
Linux 把磁盘文件的信息存放在一种叫做索引节点的内存对象中。相应的数据结构包括自己的信号量,存放在i_sem字段中。在文件系统的处理过程中会出现很多竞争条件。竞争条件都可以通过用索引节点信号量保护目录文件来避免。只要一个程序使用了两个或多个信号量,就存在死锁的可能,因为两个不同的控制路径可能互相死等着释放信号量。在有些情况下,内核必须获得两个或更多的信号量锁。索引节点信号量倾向于这种情况,例如,在rename()系统调用的服务例程中就会发生这种情况。在这种情况下,操作涉及两个不同的索引节点,因此,必须采用两个信号量。为了避免这样的死锁,信号量的请求按预先确定的地址顺序进行。
所有PC都包含一个叫实时时钟(real Time Clock RTC)的时钟,它独立于CPU和所有其他芯片的。即使当PC被切断电源,RTC还继续工作,因为它靠一个小电池或蓄电池供电。RTC能在IRQ8上周期性的发出中断,频率在2~8192Hz之间。也可以对RTC进行编程以使当RTC到达某个特定的值是激活IRQ8线,也就是作为一个闹钟来工作。Linux只用RTC来获取事件和日期,不过,通过对/dev/rtc设备文件进行操作,也允许进程对RTC编程。
现在,schedule()检查运行队列中剩余的可运行进程数。如果有可运行的进程,就调用dependent_sleeper(),绝大多数情况下,该函数立即返回 0。但是,如果内核支持超线程技术,如果被选中执行的进程优先级比已经在相同物理 CPU 的某个逻辑 CPU 上运行的兄弟进程低,则schedule()拒绝选择该进程,而执行swapper进程:
// 为大小为10的数组 分配足够的内存 // EquipmentPiece 对象; 详细情况请参见条款8 // operator new[] 函数 void *rawMemory = operatornew[](10*sizeof(EquipmentPiece)); // make bestPieces point to it so it can be treated as an // EquipmentPiece array EquipmentPiece *bestPieces = static_cast<EquipmentPiece*>(rawMemory); // construct the EquipmentPiece objects in the memory // 使用"placement new" (参见条款8) for (int i = 0; i < 10; ++i) new (&bestPieces[i]) EquipmentPiece( ID Number );
使用placement new的缺点除了是大多数程序员对它不熟悉外(能使用它就更难了),还有就是当你不想让它继续存在使用时,必须手动调用数组对象的析构函数,调用操作符delete[]来释放 raw memory(请再参见条款8):
1 2 3 4 5 6
// 以与构造bestPieces对象相反的顺序解构它。 for (int i = 9; i >= 0; --i) bestPieces[i].~EquipmentPiece(); // deallocate the raw memory operatordelete[](rawMemory);
booloperator==( const Array<int>& lhs, const Array<int>& rhs); Array<int> a(10); Array<int> b(10); ... for (int i = 0; i < 10; ++i) if (a == b[i]) { // 哎呦! "a" 应该是 "a[i]" do something for when a[i] and b[i] are equal; } else { do something for when not; }
if (expression1.operator&&(expression2)) ... // when operator&& is a // member function if (operator&&(expression1, expression2)) ... // when operator&& is a // global function
classString { ... }; // 一个string 类 (the standard // string type may be implemented // as described below, but it // doesn't have to be) String s1 = "Hello"; String s2 = s1; / 调用string拷贝构造函数
intfindCubicleNumber(const string& employeeName) { // 定义静态map,存储 (employee name, cubicle number) // pairs. 这个 map 是local cache。 typedef map<string, int> CubicleMap; static CubicleMap cubes; // try to find an entry for employeeName in the cache; // the STL iterator "it" will then point to the found // entry, if there is one (see Item 35 for details) CubicleMap::iterator it = cubes.find(employeeName); // "it"'s value will be cubes.end() if no entry was // found (this is standard STL behavior). If this is // the case, consult the database for the cubicle // number, then add it to the cache if (it == cubes.end()) { int cubicle = the result of looking up employeeName's cubicle number in the database; cubes[employeeName] = cubicle; // add the pair // (employeeName, cubicle) // to the cache return cubicle; } else { // "it" points to the correct cache entry, which is a // (employee name, cubicle number) pair. We want only // the second component of this pair, and the member // "second" will give it to us return (*it).second; } }
classAsset: public HeapTracked { private: UPNumber value; ... };
我们能够这样查询Assert*指针,如下所示:
1 2 3 4 5 6 7 8 9
voidinventoryAsset(const Asset *ap) { if (ap->isOnHeap()) { ap is a heap-based asset — inventory it as such; } else { ap is a non-heap-based asset — record it that way; } }
CD goodCD("Flood"); const CD *p; // p 是一个non-const 指针 //指向 const CD 对象 CD * const p = &goodCD; // p 是一个const 指针 // 指向non-const CD 对象; // 因为 p 是const, 它 // 必须被初始化 const CD * const p = &goodCD; // p 是一个const 指针 // 指向一个 const CD 对象
String& String::operator=(const String& rhs) { if (value == rhs.value) { // do nothing if the values return *this; // are already the same; this } // subsumes the usual test of // this against &rhs if (--value->refCount == 0) { // destroy *this's value if delete value; // no one else is using it } value = rhs.value; // have *this share rhs's ++value->refCount; // value return *this; }
char& String::operator[](int index) { // if we're sharing a value with other String objects, // break off a separate copy of the value for ourselves if (value->refCount > 1) { --value->refCount; // decrement current value's // refCount, because we won't // be using that value any more value = // make a copy of the newStringValue(value->data); // value for ourselves } // return a reference to a character inside our // unshared StringValue object return value->data[index]; }
voidSpaceShip::collide(GameObject& otherObject) { otherObject.collide(*this); } voidSpaceShip::collide(SpaceShip& otherObject) { process a SpaceShip-SpaceShip collision; } voidSpaceShip::collide(SpaceStation& otherObject) { process a SpaceShip-SpaceStation collision; } voidSpaceShip::collide(Asteroid& otherObject) { process a SpaceShip-Asteroid collision; }
extern"C" { // disable name mangling for // all the following functions voiddrawLine(int x1, int y1, int x2, int y2); voidtwiddleBits(unsignedchar bits); voidsimulate(int iterations); ... }
intmain(int argc, char *argv[]) { performStaticInitialization(); // generated by the // implementation the statements you put in main go here; performStaticDestruction(); // generated by the // implementation }
extern"C"// implement this intrealMain(int argc, char *argv[]); // function in C intmain(int argc, char *argv[])// write this in C++ { returnrealMain(argc, argv); }
int values[50]; ... int *firstFive = find(values, // search the range values+50, // values[0] - values[49] 5); // for the value 5 if (firstFive != values+50) { // did the search succeed? ... // yes } else { ... // no, the search failed }
你也可以只搜索数组的一部分:
1 2 3 4 5 6 7 8
int *firstFive = find(values, // search the range values+10, // values[0] - values[9] 5); // for the value 5 int age = 36; ... int *firstValue = find(values+10, // search the range values+20, // values[10] - values[19] age); // for the value in age
find()函数内部并没有限制它只能对int型数组操作,所以它可以实际上是一个模板:
1 2 3 4 5 6
template<class T> T * find(T *begin, T *end, const T& value) { while (begin != end && *begin != value) ++begin; return begin; }
list<char> charList; // create STL list object // for holding chars ... // find the first occurrence of 'x' in charList list<char>::iterator it = find(charList.begin(), charList.end(), 'x');
线程局部存储( Thread Local Storage,TLS)。线程局部存储是某些操作系统为线程单独提供的私有空间,但通常只具有很有限的容量。
寄存器(包括PC寄存器),寄存器是执行流的基本数据,因此为线程私有。
1.6.1.3 线程调度和优先级
当线程数量小于等于处理器数量时(并且操作系统支持多处理器),线程的并发是真正的并发
但对于线程数量大于处理器数量的情况, 此时至少有一个处理器会运行多个线程
处理器上切换不同的线程的行为称之为线程调度( Thread Schedule)
在线程调度中,线程通常拥有至少三种状态
运行( Running):此时线程正在执行。
就绪( Ready):此时线程可以立刻运行,但CPU经被占用。
等待( Waiting):此时线程正在等待某一事件(通常是IO或同步)发生,无法执行
处于运行中线程拥有一段可以执行的时间,这段时间称为时间片( Time Slice),当时间片用尽的时候,该进程将进入就绪状态。如果在时间片用尽之前进程就开始等待某事件那么它将进入等待状态。每当一个线程离开运行状态时,调度系统就会选择一个其他的就绪线程继续执行。在一个处于等待状态的线程所等待的事件发生之后,该线程将进入就绪状态
00000000 T func1 00000000 D g1oba1_init_var 00000004 C global_uninit_var 0000001b T main U printf 00000004 d static_var.1286 00000000 b static_var2.1287
Valid options for the LD_DEBUG environment variable are:
1 2 3 4 5 6 7 8 9 10
libs display library search paths reloc display relocation processing files display progress for input file symbols display symbol table processing bindings display information about symbol binding versions display version dependencies all all previous options combined statistics display relocation statistics unused determined unused DSOs help display this help message and exit
To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable.
glibc即GNU C Library,是GNU旗下的C标准库。发布版本主要由两部分组成,一部分是头文件,比如stdio.h、stdlib.h等,它们往往位于/usr/include;另外一部分则是库的二进制文件部分。二进制部分主要的就是C语言标准库,它有静态和动态两个版本。动态的标准库我们及在本书的前面章节中碰到过了,它位于/lib/libc.so.6;而静态标准库位于/usr/lib/libc.a。事实上glibc除了C标准库之外,还有几个辅助程序运行的运行库,这几个文件可以称得上是真正的“运行库”。它们就是/usr/lib/crt1.o、/usr/lib/crti.o和/usr/lib/crtn.o。是不是对这几个文件还有点印象呢?我们在第2章讲到静态库链接的时候已经碰到过它们了,虽然它们都很小,但这几个文件都是程序运行的最关键的文件。
vector、string自动管理其所包含元素的构造与析构,并有一系列的STL算法支持,同时vector也能够保证和老代码的兼容。使用了引用计数的string可以避免不必要的内存分配和字符串拷贝(COW- copy on write),但是在多线程环境里,对这个string进行线程同步的开销远大于COW的开销。此时,可以考虑使用vector<char>或动态数组。
boolisWanted(constint i); ... vector<int> myvector; vector<int>::iterator it = find_if(myvector.begin(), myvector.end(), not1(isWanted)); // error C2955: 'std::unary_function' : use of class template requires template argument list
error: aggregate 'TD<int> xType' has incomplete type and cannot be defined error: aggregate 'TD<const int *> yType' has incomplete type and cannot be defined
调用std::type_info::name不保证返回任何有意义的东西,但是库的实现者尝试尽量使它们返回的结果有用。实现者们对于“有用”有不同的理解。举个例子,GNU和Clang环境下x会显示为i,y会显示为PKi,这样的输出你必须要问问编译器实现者们才能知道他们的意义:i表示int,PK表示const to konst(const)。Microsoft的编译器输出得更直白一些:对于x输出“int“对于y输出”int const*“
enum classUserInfoFields { uiName, uiEmail, uiReputation }; UserInfo uInfo; // as before … auto val = std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)> (uInfo);
auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);
比起使用非限域枚举,限域有很多可圈可点的地方,它避免命名空间污染,防止不经意间使用隐式转换。 (下面这句我没看懂,保留原文。。(是什么典故吗。。。)) In many cases, you may decide that typing a few extra characters is a reasonable price to pay for the ability to avoid the pitfalls of an enum technology that dates to a time when the state of the art in digital telecommunications was the 2400-baud modem.
上述场景见于特殊的成员函数,即当有必要时C++自动生成的那些函数。Item 17 详细讨论了这些函数,但是现在,我们只关心拷贝构造函数和拷贝赋值运算符重载。This chapter is largely devoted to common practices in C++98 that have been superseded by better practices in C++11, and in C++98, if you want to suppress use of a member function, it’s almost always the copy constructor, the assignment operator, or both.
template <classcharT, classtraits = char_traits<charT> > class basic_ios : public ios_base { public: … private: basic_ios(const basic_ios& ); // not defined basic_ios& operator=(const basic_ios&); // not defined };
将它们声明为私有成员可以防止客户端调用这些函数。故意不定义它们意味着假如还是有代码用它们就会在链接时引发缺少函数定义(missing function definitions)错误。
上面的说法对C++11和C++98都是正确的,但是在C++98中,标准库对const_iterator的支持不是很完整。首先不容易创建它们,其次就算你有了它,它的使用也是受限的。 假如你想在std::vector<int>中查找第一次出现1983(C++代替C with classes的那一年)的位置,然后插入1998(第一个ISO C++标准被接纳的那一年)。如果vector中没有1983,那么就在vector尾部插入。在C++98中使用iterator可以很容易做到:
1 2 3 4 5
std::vector<int> values; … std::vector<int>::iterator it = std::find(values.begin(),values.end(), 1983); values.insert(it, 1998);
编译期可知的值“享有特权”,它们可能被存放到只读存储空间中。对于那些嵌入式系统的开发者,这个特性是相当重要的。更广泛的应用是“其值编译期可知”的常量整数会出现在需要“整型常量表达式( integral constant expression )的context中,这类context包括数组大小,整数模板参数(包括std::array对象的长度),枚举量,对齐修饰符(译注:alignas(val)),等等。如果你想在这些context中使用变量,你一定会希望将它们声明为constexpr,因为编译器会确保它们是编译期可知的:
也许你早已听过_Rule of Three_规则。这个规则告诉我们如果你声明了拷贝构造函数,拷贝赋值运算符,或者析构函数三者之一,你应该也声明其余两个。它来源于长期的观察,即用户接管拷贝操作的需求几乎都是因为该类会做其他资源的管理,这也几乎意味着1)无论哪种资源管理如果能在一个拷贝操作内完成,也应该在另一个拷贝操作内完成2)类析构函数也需要参与资源的管理(通常是释放)。通常意义的资源管理指的是内存(如STL容器会动态管理内存),这也是为什么标准库里面那些管理内存的类都声明了“the big three”:拷贝构造,拷贝赋值和析构。
Rule of Three带来的后果就是只要出现用户定义的析构函数就意味着简单的逐成员拷贝操作不适用于该类。接着,如果一个类声明了析构也意味着拷贝操作可能不应该自定生成,因为它们做的事情可能是错误的。在C++98提出的时候,上述推理没有得倒足够的重视,所以C++98用户声明析构不会左右编译器生成拷贝操作的意愿。C++11中情况仍然如此,但仅仅是因为限制拷贝操作生成的条件会破坏老代码。
Rule of Three规则背后的解释依然有效,再加上对声明拷贝操作阻止移动操作隐式生成的观察,使得C++11不会为那些有用户定义的析构函数的类生成移动操作。所以仅当下面条件成立时才会生成移动操作:
auto delInvmt = [](Investemnt* pInvestment) { makeLogEntry(pInvestment); delete pInvestment; }; template<typename... Ts> std::unique_ptr<Investment, decltype(delInvmt)> makeInvestment(Ts&& params) { std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt); if (/*a Stock object should be created*/) { pInv.reset(newStock(std::forward<Ts>(params)...)); } elseif ( /* a Bond object should be created */ ) { pInv.reset(newBond(std::forward<Ts>(params)...)); } elseif ( /* a RealEstate object should be created */ ) { pInv.reset(newRealEstate(std::forward<Ts>(params)...)); } return pInv; }
template<typename... Ts> makeInvestment(Ts&& params) { auto delInvmt = [](Investemnt* pInvestment) { makeLogEntry(pInvestment); delete pInvestment; }; std::unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt); if (/*a Stock object should be created*/) { pInv.reset(newStock(std::forward<Ts>(params)...)); } elseif ( /* a Bond object should be created */ ) { pInv.reset(newBond(std::forward<Ts>(params)...)); } elseif ( /* a RealEstate object should be created */ ) { pInv.reset(newRealEstate(std::forward<Ts>(params)...)); } return pInv; }
评论已经说了这是错的——或者至少大部分是错的。(错误的部分是传递this,而不是使用了emplace_back。如果你不熟悉emplace_back,参见Item42)。上面的代码可以通过编译,但是向容器传递一个原始指针(this),std::shared_ptr会由此为指向的对象(*this)创建一个控制块。那看起来没什么问题,直到你意识到如果成员函数外面早已存在指向Widget对象的指针,它是未定义行为的Game, Set, and Match(译注:一部电影,但是译者没看过。。。)。
auto spw = // after spw is constructed std::make_shared<Widget>(); // the pointed-to Widget's // ref count(RC) is 1 // See Item 21 for in on std::make_shared … std::weak_ptr<Widget> wpw(spw); // wpw points to same Widget as spw. RC remains 1 … spw = nullptr; // RC goes to 0, and the // Widget is destroyed. // wpw now dangles
std::weak_ptr用expired来表示已经dangle。你可以用它直接做测试:
1
if (wpw.expired()) … // if wpw doesn't point to an object
std::shared_ptr<const Widget> fastLoadWidget(WidgetID id) { static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache; // 译者注:这里是高亮 auto objPtr = cache[id].lock(); // objPtr is std::shared_ptr to cached object (or null if object's not in cache) if (!objPtr) { // if not in cache objPtr = loadWidget(id); // load it cache[id] = objPtr; // cache it } return objPtr; }
std::make_unique和std::make_shared有三个make functions中的两个:接收抽象参数,完美转发到构造函数去动态分配一个对象,然后返回这个指向这个对象的指针。第三个make function 是std::allocate_shared.它和std::make_shared一样,除了第一个参数是用来动态分配内存的对象。
autoupw1(std::make_unique<Widget>()); // with make func std::unique_ptr<Widget> upw2(new Widget); // without make func autospw1(std::make_shared<Widget>()); // with make func std::shared_ptr<Widget> spw2(new Widget); // without make func
classWidget { public: template<typename T> voidsetName(T&& newName){ name = std::move(newName); //universal reference compiles, but is bad ! bad ! bad ! } ... private: std::string name; std::shared_ptr<SomeDataStructure> p; };
std::string getWidgetName(); // factory function
Widget w; auto n = getWidgetName(); // n is local variiable w.setName(n); // move n into w! n's value now unkown
classWidget { public: voidsetName(const std::string& newName){ // set from const lvalue name = newName; } voidsetName(std::string&& newName){ // set from rvalue name = std::move(newName); } };
std::multiset<std::string> names; // global data structure voidlogAndAdd(const std::string& name) { auto now = std::chrono::system_lock::now(); // get current time log(now, "logAndAdd"); // make log entry names.emplace(name); // add name to global data structure; see Item 42 for info on emplace }
template<typename T> voidlogAndAdd(T&& name) { auto now = std::chrono::system_lock::now(); log(now, "logAndAdd"); names.emplace(std::forward<T>(name)); } std::string petName("Darla"); // as before logAndAdd(petName); // as before , copy logAndAdd(std::string("Persephone")); // move rvalue instead of copying it logAndAdd("Patty Dog"); // create std::string in multiset instead of copying a temporary std::string
std::string nameFromIdx(int idx); // return name corresponding to idx voidlogAndAdd(int idx) { auto now = std::chrono::system_lock::now(); log(now, "logAndAdd"); names.emplace(nameFromIdx(idx)); }
之后的两个调用按照预期工作:
1 2 3 4 5 6
std::string petName("Darla"); logAndAdd(petName); logAndAdd(std::string("Persephone")); logAndAdd("Patty Dog"); // these calls all invoke the T&& overload
std::multiset<std::string> names; // global data structure template<typename T> // make log entry and add voidlogAndAdd(T&& name) { auto now = std::chrono::system_clokc::now(); log(now, "logAndAdd"); names.emplace(std::forward<T>(name)); }
template<typename T> voidlogAndAddImpl(T&& name, std::false_type)// 高亮为std::false_type { auto now = std::chrono::system_clock::now(); log(now, "logAndAdd"); names.emplace(std::forward<T>(name)); }
一旦你理解了高亮参数的含义代码就很直观。概念上,logAndAdd传递一个布尔值给logAndAddImpl表明是否传入了一个整型类型,但是true和false是运行时值,我们需要使用编译时决策来选择正确的logAndAddImpl重载。这意味着我们需要一个类型对应true,false同理。这个需要是经常出现的,所以标准库提供了这样两个命名std::true_type and std::false_type。logAndAdd传递给logAndAddImpl的参数类型取决于T是否整型,如果T是整型,它的类型就继承自std::true_type,反之继承自std::false_type。最终的结果就是,当T不是整型类型时,这个logAndAddImpl重载会被调用。
Constraining templates that take universal references(约束使用通用引用的模板)
tag dispatch的关键是存在单独一个函数(没有重载)给客户端API。这个单独的函数分发给具体的实现函数。创建一个没有重载的分发函数通常是容易的,但是Item 26中所述第二个问题案例是Person类的完美转发构造函数,是个例外。编译器可能会自行生成拷贝和移动构造函数,所以即使你只写了一个构造函数并在其中使用tag dispatch,编译器生成的构造函数也打破了你的期望。
classPerson { // C++14 public: template< typename T, typename = std::enable_if_t< // less code here !std::is_base_of<Person, std::decay_t<T> // and here >::value > // and here > explicitPerson(T&& n); ... };
classPerson { public: template<typename T, typename = std::enable_if_t< !std::is_base_of<Person, std::decay_t<T>>::value && !std::is_integral<std::remove_reference_t<T>>::value > > explicitPerson(T&& n) :name(std::forward<T>(n)) { //assert that a std::string can be created from a T object(这里到...高亮) static_assert( std::is_constructible<std::string, T>::value, "Parameter n can't be used to construct a std::string" ); ... // the usual ctor work goes here } ... // remainder of Person class (as before) };
Widget widgetFactory(); // function returning rvalue Widget w; // a variable(an lvalue) func(w); // call func with lvalue; T deduced to be Widget& func(widgetFactory()); // call func with rvalue; T deduced to be Widget
template<typename T> voidfunc(T&& param); Widget widgetFactory(); // function returning rvalue Widget w; // a variable(an lvalue) func(w); // call func with lvalue; T deduced to be Widget& func(widgetFactory()); // call func with rvalue; T deduced to be Widget
using ProcessFuncType = int (*)(int); // make typedef; see Item 9 PorcessFuncType processValPtr = processVal; // specify needed signature for processVal fwd(processValPtr); // fine fwd(static_cast<ProcessFuncType>(workOnVal)); // alse fine
{ int x; // x is local variable ... auto c1 = [x](int y) { return x * y > 55; }; // c1 is copy of the closure produced by the lambda auto c2 = c1; // c2 is copy of c1 auto c3 = c2; // c3 is copy of c2 ... }
template<typename C> voidworkWithContainer(const C& container) { auto calc1 = computeSomeValue1(); // as above auto calc2 = computeSomeValue2(); // as above auto divisor = computeDivisor(calc1, calc2); // as above using ContElemT = typename C::value_type; // type of // elements in // container using std::begin; // for using std::end; // genericity; // see Item 13 if (std::all_of( // if all values begin(container), end(container), // in container [&](const ContElemT& value) // are multiples { return value % divisor == 0; }) // of divisor... ) { … // they are... } else { … // at least one } // isn't... }
using FilterContainer = // as before std::vector<std::function<bool(int)>>; FilterContainer filters; // as before voiddoSomeWork() { auto pw = // create Widget; see std::make_unique<Widget>(); // Item 21 for // std::make_unique pw->addFilter(); // add filter that uses // Widget::divisor … } // destroy Widget; filters // now holds dangling pointer!
voidWidget::addFilter()const { auto divisorCopy = divisor; // copy data member filters.emplace_back( [divisorCopy](int value) // capture the copy { return value % divisorCopy == 0; } // use the copy ); }
事实上如果采用这种方法,默认的按值捕获也是可行的。
1 2 3 4 5 6 7 8
voidWidget::addFilter()const { auto divisorCopy = divisor; // copy data member filters.emplace_back( [=](int value) // capture the copy { return value % divisorCopy == 0; } // use the copy ); }
auto func = [pw = std::make_unique<Widget>()] // init data mbr { return pw->isValidated() // in closure w/ && pw->isArchived(); }; // result of call // to make_unique
std::vector<double> data; // object to be moved // into closure // populate data auto func = [data = std::move(data)] { /* uses of data */ }; // C++14 init capture
Widget&& && forward(Widget& param)// instantiation of { // std::forward when returnstatic_cast<Widget&& &&>(param); // T is Widget&& } // (before reference-collapsing)
应用了引用折叠之后,代码会变成:
1 2 3 4
Widget&& forward(Widget& param)// instantiation of { // std::forward when returnstatic_cast<Widget&&>(param); // T is Widget&& } // (before reference-collapsing)
// typedef for a point in time (see Item 9 for syntax) using Time = std::chrono::steady_clock::time_point;
// see Item 10 for "enum class" enum classSound { Beep, Siren, Whistle };
// typedef for a length of time using Duration = std::chrono::steady_clock::duration; // at time t, make sound s for duration d void setAlarm(Time t, Sound s, Duration d);
// setSoundL ("L" for "lambda") is a function object allowing a // sound to be specified for a 30-sec alarm to go off an hour // after it's set auto setSoundL = [](Sound s) { // make std::chrono components available w/o qualification usingnamespace std::chrono; setAlarm(steady_clock::now() + hours(1), // alarm to go off s, // in an hour for seconds(30)); // 30 seconds };
auto setSoundL = [](Sound s) { usingnamespace std::chrono; usingnamespace std::literals; // for C++14 suffixes setAlarm(steady_clock::now() + 1h, // C++14, but s, // same meaning 30s); // as above };
usingnamespace std::chrono; // as above usingnamespace std::literals; usingnamespace std::placeholders; // needed for use of "_1" auto setSoundB = std::bind(setAlarm, // "B" for "bind" steady_clock::now() + 1h, // incorrect! see below _1, 30s);
std::threads是C++执行过程的对象,并作为软件线程的handle(句柄)。std::threads存在多种状态,1. null表示空句柄,因为处于默认构造状态(即没有函数来执行),因此不对应任何软件线程。 2. moved from (moved-to的std::thread就对应软件进程开始执行) 3. joined(连接唤醒与被唤醒的两个线程) 4. detached(将两个连接的线程分离)
需要访问非常基础的线程API。C++并发API通常是通过操作系统提供的系统级API(pthreads 或者 windows threads)来实现的,系统级API通常会提供更加灵活的操作方式,举个例子,C++并发API没有线程优先级和affinities的概念。为了提供对底层系统级线程API的访问,std::thread对象提供了native_handle的成员函数,而在高层抽象的比如std::futures没有这种能力。
auto fut1 = std::async(f); // run f using default launch policy auto fut2 = std::async(std::launch::async | std::launch::deferred, f); // run f either async or defered
usingnamespace std::literals; // for C++14 duration suffixes; see Item 34 voidf() { std::this_thread::sleep_for(1s); }
auto fut = std::async(f); while (fut.wait_for(100ms) != std::future_status::ready) { // loop until f has finished running... which may never happen! ... }
auto fut = std::async(f); if (fut.wait_for(0s) == std::future_status::deferred) { // if task is deferred ... // use wait or get on fut to call f synchronously } else { // task isn't deferred while(fut.wait_for(100ms) != std::future_status::ready) { // infinite loop not possible(assuming f finished) ... // task is neither deferred nor ready, so do concurrent word until it's ready } }
这些各种考虑的结果就是,只要满足以下条件,std::async的默认启动策略就可以使用:
task不需要和执行get or wait的线程并行执行
不会读写线程的线程本地变量
可以保证在std::async返回的将来会调用get or wait,或者该任务可能永远不会执行是可以接受的
每个std::thread对象处于两个状态之一:joinable or unjoinable。joinable状态的std::thread对应于正在运行或者可能正在运行的异步执行线程。比如,一个blocked或者等待调度的std::thread是joinable,已运行结束的std::thread也可以认为是joinable
constexprauto tenMillion = 10000000; // see Item 15 for constexpr booldoWork(std::function<bool(int)> filter, int maxVal = tenMillion)// return whether computation was performed; see Item2 for std::function { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); auto nh = t.native_handle(); // use t's native handle to set t's priority ... if (conditionsAreStatisfied()) { t.join(); // let t finish performComputation(goodVals); // computation was performed returntrue; } returnfalse; // computation was not performed }
这使你有责任确保使用std::thread对象时,在所有的路径上最终都是unjoinable的。但是覆盖每条路径可能很复杂,可能包括return, continue, break, goto or exception,有太多可能的路径。
每当你想每条路径的块之外执行某种操作,最通用的方式就是将该操作放入本地对象的析构函数中。这些对象称为RAII对象,通过RAII类来实例化。(RAII全称为 Resource Acquisition Is Initialization)。RAII类在标准库中很常见。比如STL容器,智能指针,std::fstream类等。但是标准库没有RAII的std::thread类,可能是因为标准委员会拒绝将join和detach作为默认选项,不知道应该怎么样完成RAII。
在ThreadRAII析构函数调用std::thread对象t的成员函数之前,检查t是否joinable。这是必须的,因为在unjoinbale的std::thread上调用join or detach会导致未定义行为。客户端可能会构造一个std::threadt,然后通过t构造一个ThreadRAII,使用get获取t,然后移动t,或者调用join or detach,每一个操作都使得t变为unjoinable 如果你担心下面这段代码
1 2 3 4 5 6 7
if (t.joinable()) { if (action == DtorAction::join) { t.join(); } else { t.detach(); } }
存在竞争,因为在t.joinable()和t.join or t.detach执行中间,可能有其他线程改变了t为unjoinable,你的态度很好,但是这个担心不必要。std::thread只有自己可以改变joinable or unjoinable的状态。在ThreadRAII的析构函数中被调用时,其他线程不可能做成员函数的调用。如果同时进行调用,那肯定是有竞争的,但是不在析构函数中,是在客户端代码中试图同时在一个对象上调用两个成员函数(析构函数和其他函数)。通常,仅当所有都为const成员函数时,在一个对象同时调用两个成员函数才是安全的。
在doWork的例子上使用ThreadRAII的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
booldoWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; ThreadRAII t(std::thread([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }), ThreadRAII::DtorAction::join ); auto nh = t.get().native_handle(); ... if (conditonsAreStatisfied()) { t.get().join(); performComputation(goodVals); returntrue; } returnfalse; }
// this container might block in its dtor, because one or more contained futures could refer to a shared state for a non-deferred task launched via std::async std::vector<std::future<void>> futs; // see Item 39 for info on std::future<void> classWidget// Widget objects might block in their dtors { public: ... private: std::shared_future<double> fut; };
intcalcValue(); // func to run std::packaged_task<int()> pt(calcValue); // wrap calcValue so it can run asynchrously auto fut = pt.get_future(); // get future for pt
反应任务对的代码稍微复杂一点,因为在调用wait条件变量之前,必须通过std::unique_lock对象使用互斥锁mutex来同步(lock a mutex是等待条件变量的经典实现。std::unique_lock是C++11的易用API),代码如下:
1 2 3 4 5 6 7
... // propare to react { // open critical section std::unique_lock<std::mutex> lk(m); // lock mutex cv.wait(lk); // wati for notify; this isn't correct! ... // react to event(m is blocked) } // close crit. section; unlock m via lk's dtor ... // continue reacting (m now unblocked)
现在,std::promise和futures(std::future and std::shared_future)都是需要参数类型的模板。参数表明被传递的信息类型。在这里,没有数据被传递,只需要让反应任务知道future已经被设置了。我们需要的类型是表明在std::promise和futures之间没有数据被传递。所以选择void。检测任务使用std::promise<void>,反应任务使用std::future<void> or std::shared_future<void>。当感兴趣的事件发生时,检测任务设置std::promise<void>,反应任务在futures上等待。即使反应任务不接收任何数据,通信信道可以让反应任务知道检测任务是否设置了void数据(通过对std::promise<void>调用set_value)。
所以,代码如下:
1
std::promise<void> p; // promise for communications channel
std::promise<void> p; voidreact(); // func for reacting task voiddetect()// func for detecting task { st::thread t([] // create thread { p.get_future().wait(); // suspend t until future is set react(); }); ... // here, t is suspended prior to call to react p.set_value(); // unsuspend t (and thus call react) ... // do additional work t.join(); // make t unjoinable(see Item 37) }
std::promise<void> p; // as before voiddetect()// now for multiple reacting tasks { auto sf = g.get_future().share(); // sf's type is std::shared_future<void> std::vector<std::thread> vt; // container for reacting threads for (int i = 0; i < threadsToRun; ++i) { vt.emplace_back([sf]{ sf.wait(); react(); }); // wait on local copy of sf; see Item 43 for info on emplace_back } ... // detect hangs if this "..." code throws ! p.set_value(); // unsuspend all threads ... for (auto& t : vt) { t.join(); // make all threads unjoinable: see Item2 for info on "auto&" } }
std::atomic<int> ai(0); // initialize ai to 0 ai = 10; // atomically set ai to 10 std::cout << ai; // atomically read ai's value ++ai; //atomically increment ai to 11 --ai; // atomically decrement ai to 10
volatileintvi(0); // initalize vi to 0 vi = 10; // set vi to 10 std::cout << vi; // read vi's value ++vi; // increment vi to 11 --vi; // decrement vi to 10
register = x.load(); // read x into register std::atomic<int> y(register); // init y with register value y.store(register); // store register value into y
对于C++中的通用技术,总是存在适用场景。除了本章覆盖的两个例外,描述什么场景使用哪种通用技术通常来说很容易。这两个例外是传值(pass by value)和 emplacement。决定何时使用这两种技术受到多种因素的影响,本书提供的最佳建议是在使用它们的同时仔细考虑清楚,尽管它们都是高效的现代C++编程的重要角色。接下来的Items提供了是否使用它们来编写软件的所需信息。
Item 41.Consider pass by value for copyable parameters that are cheap to move and always copied 如果参数可拷贝并且移动操作开销很低,总是考虑直接按值传递
Using a universal reference(通用模板方式):同重载一样,调用也绑定到addName的引用实现上,没有开销。由于使用了std::forward,左值参数会复制到Widget::names,右值参数移动进去。开销总结同重载方式。 Item25 解释了如果调用者传递的参数不是std::string类型,将会转发到std::string的构造函数(几乎是零开销的拷贝或者移动操作)。因此通用引用的方式同样有同样效率,所以者不影响本次分析,简单分析std::string参数类型即可。
Passing by value(按值传递):无论传递左值还是右值,都必须构造newName参数。如果传递的是左值,需要拷贝的开销,如果传递的是右值,需要移动的开销。在函数的实现中,newName总是采用移动的方式到Widget::names。开销总结:左值参数,一次拷贝一次移动,右值参数两次移动。对比按引动传递的方法,对于左值或者右值,均多出一次移动操作。
再次回顾本Item的内容:
1
总是考虑直接按值传递,如果参数可拷贝并且移动操作开销很低
这样措辞是有原因的:
应该仅consider using pass by value。是的,因为只需要编写一个函数,同时只会在目标代码中生成一个函数。避免了通用引用方式的种种问题。但是毕竟开销会更高,而且下面还会讨论,还会存在一些目前我们并未讨论到的开销。
vs.emplace_back("xyzzy"); // construct new value at end of container; don't pass the type in container; don't use container rejecting duplicates vs.emplace_back(50, 'x'); // ditto
regexes.emplace_back(nullptr); // compiles. Direct init permits use of explicit std::regex ctor taking a pointer regexes.push_back(nullptr); // error! copy init forbids use of that ctor
/* 计算 p[j..] 是否匹配 s[i..] */ booldp(string& s, int i, string& p, int j){ int m = s.size(), n = p.size(); // base case if (j == n) { return i == m; } if (i == m) { if ((n - j) % 2 == 1) { returnfalse; } for (; j + 1 < n; j += 2) { if (p[j + 1] != '*') { returnfalse; } } returntrue; }
动态规划的时间复杂度为「状态的总数」「每次递归花费的时间」,本题中状态的总数当然就是i和j的组合,也就是M N(M为s的长度,N为p的长度);递归函数dp中没有循环(base case 中的不考虑,因为 base case 的触发次数有限),所以一次递归花费的时间为常数。二者相乘,总的时间复杂度为O(MN)。空间复杂度很简单,就是备忘录memo的大小,即O(MN)。
Client 发送 SYN 包给 Server 后挂了,Server 回给 Client 的 SYN-ACK 一直没收到 Client 的 ACK 确认,这个时候这个连接既没建立起来,也不能算失败。这就需要一个超时时间让 Server 将这个连接断开,否则这个连接就会一直占用 Server 的 SYN 连接队列中的一个位置,大量这样的连接就会将 Server 的 SYN 连接队列耗尽,让正常的连接无法得到处理。目前,Linux 下默认会进行 5 次重发 SYN-ACK 包,重试的间隔时间从 1s 开始,下次的重试间隔时间是前一次的双倍,5 次的重试时间间隔为 1s,2s, 4s, 8s,16s,总共 31s,第 5 次发出后还要等 32s 都知道第 5 次也超时了,所以,总共需要 1s + 2s +4s+ 8s+ 16s + 32s =63s,TCP 才会把断开这个连接。 由于,SYN 超时需要 63 秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的 SYN 包给 Server(俗称 SYN flood 攻击),用于耗尽 Server 的 SYN 队列。对于应对 SYN 过多的问题,linux 提供了几个 TCP 参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。
疑症(4) TCP 的 Peer 两端同时断开连接
由上面的”TCP 协议状态机”图可以看出,TCP 的 Peer 端在收到对端的 FIN 包前发出了 FIN 包,那么该 Peer 的状态就变成了 FIN_WAIT1,Peer 在 FIN_WAIT1 状态下收到对端 Peer 对自己 FIN 包的 ACK 包的话,那么 Peer 状态就变成 FIN_WAIT2,Peer 在 FIN_WAIT2 下收到对端 Peer 的 FIN 包,在确认已经收到了对端 Peer 全部的 Data 数据包后,就响应一个 ACK 给对端 Peer,然后自己进入 TIME_WAIT 状态。 但是如果 Peer 在 FIN_WAIT1 状态下首先收到对端 Peer 的 FIN 包的话,那么该 Peer 在确认已经收到了对端 Peer 全部的 Data 数据包后,就响应一个 ACK 给对端 Peer,然后自己进入 CLOSEING 状态,Peer 在 CLOSEING 状态下收到自己的 FIN 包的 ACK 包的话,那么就进入 TIME WAIT 状态。于是,TCP 的 Peer 两端同时发起 FIN 包进行断开连接,那么两端 Peer 可能出现完全一样的状态转移 FIN_WAIT1——>CLOSEING——->TIME_WAIT,也就会 Client 和 Server 最后同时进入 TIME_WAIT 状态。同时关闭连接的状态转移如下图所示:
疑症(5)四次挥手能不能变成三次挥手呢??
答案是可能的。TCP 是全双工通信,Cliet 在自己已经不会在有新的数据要发送给 Server 后,可以发送 FIN 信号告知 Server,这边已经终止 Client 到对端 Server 那边的数据传输。但是,这个时候对端 Server 可以继续往 Client 这边发送数据包。于是,两端数据传输的终止在时序上是独立并且可能会相隔比较长的时间,这个时候就必须最少需要 2+2= 4 次挥手来完全终止这个连接。但是,如果 Server 在收到 Client 的 FIN 包后,在也没数据需要发送给 Client 了,那么对 Client 的 ACK 包和 Server 自己的 FIN 包就可以合并成为一个包发送过去,这样四次挥手就可以变成三次了(似乎 linux 协议栈就是这样实现的)
疑症(6) TCP 的头号疼症 TIME_WAIT 状态
要说明 TIME_WAIT 的问题,需要解答以下几个问题
Peer 两端,哪一端会进入 TIME_WAIT 呢?为什么?
相信大家都知道,TCP 主动关闭连接的那一方会最后进入 TIME_WAIT。那么怎么界定主动关闭方呢?是否主动关闭是由 FIN 包的先后决定的,就是在自己没收到对端 Peer 的 FIN 包之前自己发出了 FIN 包,那么自己就是主动关闭连接的那一方。对于疑症(4)中描述的情况,那么 Peer 两边都是主动关闭的一方,两边都会进入 TIME_WAIT。为什么是主动关闭的一方进行 TIME_WAIT 呢,被动关闭的进入 TIME_WAIT 可以不呢?我们来看看 TCP 四次挥手可以简单分为下面三个过程:
过程一.主动关闭方发送 FIN;
过程二.被动关闭方收到主动关闭方的 FIN 后发送该 FIN 的 ACK,被动关闭方发送 FIN;
过程三.主动关闭方收到被动关闭方的 FIN 后发送该 FIN 的 ACK,被动关闭方等待自己 FIN 的 ACK。
问题就在过程三中,据 TCP 协议规范,不对 ACK 进行 ACK,如果主动关闭方不进入 TIME_WAIT,那么主动关闭方在发送完 ACK 就走了的话,如果最后发送的 ACK 在路由过程中丢掉了,最后没能到被动关闭方,这个时候被动关闭方没收到自己 FIN 的 ACK 就不能关闭连接,接着被动关闭方会超时重发 FIN 包,但是这个时候已经没有对端会给该 FIN 回 ACK,被动关闭方就无法正常关闭连接了,所以主动关闭方需要进入 TIME_WAIT 以便能够重发丢掉的被动关闭方 FIN 的 ACK。
TIME_WAIT 状态是用来解决或避免什么问题呢?
TIME_WAIT 主要是用来解决以下几个问题:
上面解释为什么主动关闭方需要进入 TIME_WAIT 状态中提到的:主动关闭方需要进入 TIME_WAIT 以便能够重发丢掉的被动关闭方 FIN 包的 ACK。如果主动关闭方不进入 TIME_WAIT,那么在主动关闭方对被动关闭方 FIN 包的 ACK 丢失了的时候,被动关闭方由于没收到自己 FIN 的 ACK,会进行重传 FIN 包,这个 FIN 包到主动关闭方后,由于这个连接已经不存在于主动关闭方了,这个时候主动关闭方无法识别这个 FIN 包,协议栈会认为对方疯了,都还没建立连接你给我来个 FIN 包?,于是回复一个 RST 包给被动关闭方,被动关闭方就会收到一个错误(我们见的比较多的:connect reset by peer,这里顺便说下 Broken pipe,在收到 RST 包的时候,还往这个连接写数据,就会收到 Broken pipe 错误了),原本应该正常关闭的连接,给我来个错误,很难让人接受。
防止已经断开的连接 1 中在链路中残留的 FIN 包终止掉新的连接 2(重用了连接 1 的所有的 5 元素(源 IP,目的 IP,TCP,源端口,目的端口)),这个概率比较低,因为涉及到一个匹配问题,迟到的 FIN 分段的序列号必须落在连接 2 的一方的期望序列号范围之内,虽然概率低,但是确实可能发生,因为初始序列号都是随机产生的,并且这个序列号是 32 位的,会回绕。
防止链路上已经关闭的连接的残余数据包(a lost duplicate packet or a wandering duplicate packet) 干扰正常的数据包,造成数据流的不正常。这个问题和 2)类似。
其中:UBOUND 是 RTO 值的上限;例如:可以定义为 1 分钟,LBOUND 是 RTO 值的下限,例如,可以定义为 1 秒;ALPHA is a smoothing factor (e.g., .8 to .9), and BETA is a delay variance factor(e.g., 1.3 to 2.0).
对于 SYN 半连接队列的大小是由(/proc/sys/net/ipv4/tcp_max_syn_backlog)这个内核参数控制的,有些内核似乎也受 listen 的 backlog 参数影响,取得是两个值的最小值。当这个队列满了,Server 会丢弃新来的 SYN 包,而 Client 端在多次重发 SYN 包得不到响应而返回(connection time out)错误。但是,当 Server 端开启了 syncookies,那么 SYN 半连接队列就没有逻辑上的最大值了,并且/proc/sys/net/ipv4/tcp_max_syn_backlog 设置的值也会被忽略。
A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59). Each LED represents a zero or one, with the least significant bit on the right.
Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the watch could represent.
classSolution { public: voidDFS(int len, int k, int curIndex, int val, vector<int>& vec) { if(k==0 && len==4 && val < 12) vec.push_back(val); if(k==0 && len==6 && val < 60) vec.push_back(val); if(curIndex == len || k == 0) return; DFS(len, k, curIndex+1, val, vec); val += pow(2, curIndex), k--, curIndex++; DFS(len, k, curIndex, val, vec); } vector<string> readBinaryWatch(int num){ vector<string> ans; for(int i = max(0, num-6); i <= min(4, num); i++) { vector<int> vec1, vec2; DFS(4, i, 0, 0, vec1), DFS(6, num-i, 0, 0, vec2); for(auto val1: vec1) for(auto val2: vec2) { string str = (to_string(val2).size()==1?"0":"") + to_string(val2); ans.push_back(to_string(val1)+":"+ str); } } return ans; } };
Leetcode402. Remove K Digits
Given string num representing a non-negative integer num, and an integer k, return the smallest possible integer after removing k digits from num.
Example 1:
1 2 3
Input: num = "1432219", k = 3 Output: "1219" Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.
Example 2:
1 2 3
Input: num = "10200", k = 1 Output: "200" Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.
Example 3:
1 2 3
Input: num = "10", k = 2 Output: "0" Explanation: Remove all the digits from the number and it is left with nothing which is 0.
我们开始遍历给定数字 num 的每一位,对于当前遍历到的数字c,进行如下 while 循环,如果 res 不为空,且k大于0,且 res 的最后一位大于c,那么应该将 res 的最后一位移去,且k自减1。当跳出 while 循环后,我们将c加入 res 中,最后将 res 的大小重设为 n-k。根据题目中的描述,可能会出现 “0200” 这样不符合要求的情况,所以我们用一个 while 循环来去掉前面的所有0,然后返回时判断是否为空,为空则返回 “0”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: string removeKdigits(string num, int k){ string res = ""; int len = num.length(); for (int i = 0; i < len; i ++) { while (k > 0 && res.length() > 0 && num[i] < res.back()) { res.pop_back(); k --; } if (res.length() > 0 || num[i] != '0') res += num[i]; } while(res.size() > 0 && k--) res.pop_back(); return res.empty() ? "0" : res; } };
Leetcode403. Frog Jump
A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.
Given a list of stones’ positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit.
If the frog’s last jump was k units, then its next jump must be either k - 1, k , or k + 1 units. Note that the frog can only jump in the forward direction.
Note:
The number of stones is ≥ 2 and is < 1,100.
Each stone’s position will be a non-negative integer < 231.
The first stone’s position is always 0.
Example 1:
1 2 3 4 5 6 7 8 9 10 11
[0,1,3,5,6,8,12,17]
There are a total of 8 stones. The first stone at the 0th unit, second stone at the 1st unit, third stone at the 3rd unit, and so on... The last stone at the 17th unit.
Return true. The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone.
Example 2:
1 2 3 4
[0,1,2,3,4,8,9,11]
Return false. There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large.
Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used.
Note:
All letters in hexadecimal (a-f) must be in lowercase.
The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character ‘0’; otherwise, the first character in the hexadecimal string will not be the zero character.
The given number is guaranteed to fit within the range of a 32-bit signed integer.
You must not use any method provided by the library which converts/formats the number to hex directly.
classSolution { public: string toHex(int num){ string res = ""; if(num == 0) return"0"; char digits[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; if(num < 0) { num = abs(num); num = ~num + 1; } int count = 8; while(count --) { int temp = 15 & num; num = num >> 4; res = digits[temp] + res; if(num == 0) break; cout << digits[temp] << " " << num << endl; } return res; } };
Leetcode406. Queue Reconstruction by Height
You are given an array of people, people, which are the attributes of some people in a queue (not necessarily in order). Each people[i] = [hi, ki] represents the ith person of height hi with exactly ki other people in front who have a height greater than or equal to hi.
Reconstruct and return the queue that is represented by the input array people. The returned queue should be formatted as an array queue, where queue[j] = [hj, kj] is the attributes of the jth person in the queue (queue[0] is the person at the front of the queue).
Example 1:
1 2
Input: people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] Output: [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
Explanation:
Person 0 has height 5 with no other people taller or the same height in front.
Person 1 has height 7 with no other people taller or the same height in front.
Person 2 has height 5 with two persons taller or the same height in front, which is person 0 and 1.
Person 3 has height 6 with one person taller or the same height in front, which is person 1.
Person 4 has height 4 with four people taller or the same height in front, which are people 0, 1, 2, and 3.
Person 5 has height 7 with one person taller or the same height in front, which is person 1.
Hence [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] is the reconstructed queue.
这道题给了我们一个队列,队列中的每个元素是一个 pair,分别为身高和前面身高不低于当前身高的人的个数,让我们重新排列队列,使得每个 pair 的第二个参数都满足题意。首先来看一种超级简洁的方法,给队列先排个序,按照身高高的排前面,如果身高相同,则第二个数小的排前面。然后新建一个空的数组,遍历之前排好序的数组,然后根据每个元素的第二个数字,将其插入到 res 数组中对应的位置,参见代码如下:
Given an m x n integer matrix heightMap representing the height of each unit cell in a 2D elevation map, return the volume of water it can trap after raining.
Example 1:
1 2 3 4 5
Input: heightMap = [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] Output: 4 Explanation: After the rain, water is trapped between the blocks. We have two small pounds 1 and 3 units trapped. The total volume of water trapped is 4.
classSolution { public: inttrapRainWater(vector<vector<int>>& heights){ if (heights.size() == 0) return0; int m = heights.size(), n = heights[0].size(), res = 0; vector<vector<bool> > visited(m, vector<bool>(n, false)); priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; vector<vector<int>> dir{{0,-1},{-1,0},{0,1},{1,0}}; for (int i = 0; i < m; i ++) for (int j = 0; j < n; j ++) if (i == 0 || i == m-1 || j == 0 || j == n-1) { q.push({heights[i][j], i*n+j}); visited[i][j] = true; } int max_height = 0; while(!q.empty()) { pair<int, int> t = q.top(); q.pop(); int h = t.first, r = t.second / n, c = t.second % n; max_height = max(max_height, h); for (int i = 0; i < 4; i ++) { int x = r + dir[i][0]; int y = c + dir[i][1]; if (x < 0 || x >= m || y < 0 || y >= n || visited[x][y]) continue; visited[x][y] = true; if (heights[x][y] < max_height) res = res + (max_height - heights[x][y]); q.push({heights[x][y], x*n+y}); } } return res; } };
Leetcode409. Longest Palindrome
Given a string which consists of lowercase or uppercase letters, find the length of the longest palindromes that can be built with those letters.
This is case sensitive, for example “Aa” is not considered a palindrome here.
Note: Assume the length of given string will not exceed 1,010.
Example:
1 2 3 4
Input: "abccccdd" Output: 7 Explanation: One longest palindrome that can be built is "dccaccd", whose length is 7.
classSolution { public: intlongestPalindrome(string s){ int ch[128] = {0}; for(char c : s) ch[c]++; int ans = 0; for(int i : ch) { ans += (i / 2 * 2); if(ans % 2== 0 && i % 2 == 1) ans ++; } return ans; } };
Leetcode410. Split Array Largest Sum
Given an array which consists of non-negative integers and an integer m , you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays.
Note: Given m satisfies the following constraint: 1 ≤ m ≤ length(nums) ≤ 14,000.
Examples:
1 2 3 4 5 6
Input: nums = [7,2,5,10,8] m = 2
Output: 18
Explanation:
There are four ways to split nums into two subarrays.
The best way is to split it into [7,2,5] and [10,8],
where the largest sum among the two subarrays is only 18.
classSolution { public: intsplitArray(vector<int>& nums, int m){ long left = 0, right = 0; for (int i = 0; i < nums.size(); ++i) { left = max(left, (long)nums[i]); right += nums[i]; } while (left < right) { longlong mid = left + (right - left) / 2; if (can_split(nums, m, mid)) right = mid; else left = mid + 1; } return right; } boolcan_split(vector<int>& nums, long m, long sum){ long cnt = 1, curSum = 0; for (int i = 0; i < nums.size(); ++i) { curSum += nums[i]; if (curSum > sum) { curSum = nums[i]; ++cnt; if (cnt > m) returnfalse; } } returntrue; } };
classSolution { public: intsplitArray(vector<int>& nums, int m){ int n = nums.size(); vector<long> sums(n + 1); vector<vector<long>> dp(m + 1, vector<long>(n + 1, LONG_MAX)); dp[0][0] = 0; for (int i = 1; i <= n; ++i) { sums[i] = sums[i - 1] + nums[i - 1]; } for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { for (int k = i - 1; k < j; ++k) { long val = max(dp[i - 1][k], sums[j] - sums[k]); dp[i][j] = min(dp[i][j], val); } } } return dp[m][n]; } };
Leetcode412. Fizz Buzz
Write a program that outputs the string representation of numbers from 1 to n.
But for multiples of three it should output “Fizz” instead of the number and for the multiples of five output “Buzz”. For numbers which are multiples of both three and five output “FizzBuzz”.
An integer array is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.
For example, [1,3,5,7,9], [7,7,7,7], and [3,-1,-5,-9] are arithmetic sequences. Given an integer array nums, return the number of arithmetic subarrays of nums.
A subarray is a contiguous subsequence of the array.
Example 1:
1 2 3
Input: nums = [1,2,3,4] Output: 3 Explanation: We have 3 arithmetic slices in nums: [1, 2, 3], [2, 3, 4] and [1,2,3,4] itself.
Example 2:
1 2
Input: nums = [1] Output: 0
这道题让我们算一种算数切片,说白了就是找等差数列,限定了等差数列的长度至少为3,那么[1,2,3,4]含有3个长度至少为3的算数切片,我们再来看[1,2,3,4,5]有多少个呢: len = 3: [1,2,3], [2,3,4], [3,4,5]
classSolution { public: intnumberOfArithmeticSlices(vector<int>& A){ int res = 0, len = 2, n = A.size(); for (int i = 2; i < n; ++i) { if (A[i] - A[i - 1] == A[i - 1] - A[i - 2]) { ++len; } else { if (len > 2) res += (len - 1) * (len - 2) * 0.5; len = 2; } } if (len > 2) res += (len - 1) * (len - 2) * 0.5; return res; } };
Leetcode414. Third Maximum Number
Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n).
Example 1:
1 2 3
Input: [3, 2, 1] Output: 1 Explanation: The third maximum is 1.
Example 2:
1 2 3
Input: [1, 2] Output: 2 Explanation: The third maximum does not exist, so the maximum (2) is returned instead.
Example 3:
1 2 3 4
Input: [2, 2, 3, 1] Output: 1 Explanation: Note that the third maximum here means the third maximum distinct number. Both numbers with value 2 are both considered as second maximum.
classSolution { public: string addStrings(string num1, string num2){ // computing lengths of both strings int num1length = num1.length() - 1; int num2length = num2.length()- 1; // solution string string sol = ""; // remainder for when summing two numbers int remainder = 0; // while both strings haven't been consumed while (num1length >= 0 || num2length >= 0) { // get current characters of iteration int current_num1 = (num1length >= 0) ? num1.at(num1length) - '0' : 0; int current_num2 = (num2length >= 0) ? num2.at(num2length) - '0' : 0; // appending sum and remainder from previous to solution string int sum = current_num1 + current_num2 + remainder; sol = std::to_string(sum % 10) + sol; // determining whether there's a remainder for next sum remainder = (sum > 9) ? 1 : 0; // decrementing for next addition num1length--; num2length--; } // append final remainder if there is one sol = (remainder == 1) ? std::to_string(1) + sol : sol; return sol; } };
Leetcode416. Partition Equal Subset Sum
Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Example 1:
1 2 3
Input: nums = [1,5,11,5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
1 2 3
Input: nums = [1,2,3,5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
classSolution { public: boolcanPartition(vector<int>& nums){ int sum = accumulate(nums.begin(), nums.end(), 0), target = sum >> 1; if (sum & 1) returnfalse; vector<bool> dp(target + 1, false); dp[0] = true; for (int num : nums) { for (int i = target; i >= num; --i) { dp[i] = dp[i] || dp[i - num]; } } return dp[target]; } };
Leetcode417. Pacific Atlantic Water Flow
There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island’s left and top edges, and the Atlantic Ocean touches the island’s right and bottom edges.
The island is partitioned into a grid of square cells. You are given an m x n integer matrix heights where heights[r][c] represents the height above sea level of the cell at coordinate (r, c).
The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell’s height is less than or equal to the current cell’s height. Water can flow from any cell adjacent to an ocean into the ocean.
Return a 2D list of grid coordinates result where result[i] = [ri, ci] denotes that rain water can flow from cell (ri, ci) to both the Pacific and Atlantic oceans.
classSolution { public: vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) { int m = heights.size(); if (m == 0) return {}; int n = heights[0].size(); vector<vector<int>> res; vector<vector<bool>> p_flags(m, vector<bool>(n ,false)); vector<vector<bool>> a_flags(m, vector<bool>(n ,false)); for (int i = 0; i < m; i ++) { dfs(heights, p_flags, i, 0); dfs(heights, a_flags, i, n-1); } for (int i = 0; i < n; i ++) { dfs(heights, p_flags, 0, i); dfs(heights, a_flags, m-1, i); } for (int i = 0; i < m; i ++) for (int j = 0; j < n; j ++) if (p_flags[i][j] && a_flags[i][j]) { vector<int> t; t.push_back(i); t.push_back(j); res.push_back(t); } return res; } voiddfs(vector<vector<int>>& heights, vector<vector<bool>>& flags, int i, int j){ int m = heights.size(), n = heights[0].size(); vector<pair<int, int>> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; flags[i][j] = true; for (int ii = 0; ii < 4; ii ++) { int x = i + dirs[ii].first; int y = j + dirs[ii].second; if (x >= 0 && x < m && y >= 0 && y < n && !flags[x][y] && heights[x][y] >= heights[i][j]) { flags[x][y] = true; dfs(heights, flags, x, y); } } } };
Leetcode419. Battleships in a Board
Given an 2D board, count how many battleships are in it. The battleships are represented with ‘X’s, empty slots are represented with ‘.’s. You may assume the following rules: You receive a valid board, made of only battleships or empty slots. Battleships can only be placed horizontally or vertically. In other words, they can only be made of the shape 1xN (1 row, N columns) or Nx1 (N rows, 1 column), where N can be of any size. At least one horizontal or vertical cell separates between two battleships - there are no adjacent battleships. Example:
1 2 3
X..X ...X ...X
In the above board there are 2 battleships. Invalid Example:
1 2 3
...X XXXX ...X
This is an invalid board that you will not receive - as battleships will always have a cell separating between them. Follow up: Could you do it in one-pass, using only O(1) extra memory and without modifying the value of the board?
利用最简单的方法找有多少个X块,这里用的遍历很方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intcountBattleships(vector<vector<char>>& board){ int x = board.size(); int y = board[0].size(); int res=0; for(int i=0;i<x;i++) for(int j=0;j<y;j++) if(board[i][j]=='X'){ if(j<y-1 && board[i][j+1]=='X') continue; if(i<x-1 && board[i+1][j]=='X') continue; res++; } return res; } };
Leetcode421. Maximum XOR of Two Numbers in an Array
Given an integer array nums, return the maximum result of nums[i] XOR nums[j], where 0 <= i <= j < n.
Example 1:
1 2 3
Input: nums = [3,10,5,25,2,8] Output: 28 Explanation: The maximum result is 5 XOR 25 = 28.
classSolution { public: structNode { int son[2]; Node() { son[0] = son[1] = -1; } }; vector<Node> nodes; intfindMaximumXOR(vector<int>& a){ // 让异或的结果最高位尽可能大 // 从高位到低位考虑,ai当前这个位为0,那希望aj当前位为1 // 用前缀树 if (a.size() == 0) return0; nodes.push_back({}); insert(a.back()); int ret = 0; for (int i = a.size()-2; i >= 0; i --) { int ans = query(a[i]); ret = max(ret, ans); insert(a[i]); } return ret; } voidinsert(int a){ int id = 0; for (int i = 31; i >= 0; i --) { int t = (a >> i) & 1; if (nodes[id].son[t] == -1) { nodes.push_back({}); nodes[id].son[t] = nodes.size()-1; } id = nodes[id].son[t]; } } intquery(int x){ int ret = 0; int id = 0; for (int i = 31; i >= 0; i --) { int b = (x >> i) & 1; int b2 = b ^ 1; if (nodes[id].son[b2] != -1) { id = nodes[id].son[b2]; ret |= (1 << i); } else { id = nodes[id].son[b]; } } return ret; } };
Leetcode423. Reconstruct Original Digits from English
Given a non-empty string containing an out-of-order English representation of digits 0-9, output the digits in ascending order.
Note:
Input contains only lowercase English letters.
Input is guaranteed to be valid and can be transformed to its original digits. That means invalid inputs such as “abc” or “zerone” are not permitted.
classSolution { public: string originalDigits(string s){ string res = ""; vector<string> words{"zero", "two", "four", "six", "eight", "one", "three", "five", "seven", "nine"}; vector<int> nums{0, 2, 4, 6, 8, 1, 3, 5, 7, 9}, counts(26, 0); vector<char> chars{'z', 'w', 'u', 'x', 'g', 'o', 'h', 'f', 's', 'i'}; for (char c : s) ++counts[c - 'a']; for (int i = 0; i < 10; ++i) { int cnt = counts[chars[i] - 'a']; for (int j = 0; j < words[i].size(); ++j) { counts[words[i][j] - 'a'] -= cnt; } while (cnt--) res += (nums[i] + '0'); } sort(res.begin(), res.end()); return res; } };
Leetcode424. Longest Repeating Character Replacement
Given a string that consists of only uppercase English letters, you can replace any letter in the string with another letter at most k times. Find the length of a longest substring containing all repeating letters you can get after performing the above operations.
Note: Both the string’s length and k will not exceed 10 4.
Example 1:
1 2 3 4 5 6 7 8
Input: s = "ABAB", k = 2
Output: 4
Explanation: Replace the two 'A's with two 'B's or vice versa.
Example 2:
1 2 3 4 5 6 7 8 9
Input: s = "AABABBA", k = 1
Output: 4
Explanation: Replace the one 'A' in the middle with 'B' and form "AABBBBA". The substring "BBBB" has the longest repeating letters, which is 4.
这道题给我们了一个字符串,说我们有k次随意置换任意字符的机会,让我们找出最长的重复字符的字符串。我们首先来想,如果没有k的限制,让我们求把字符串变成只有一个字符重复的字符串需要的最小置换次数,那么就是字符串的总长度减去出现次数最多的字符的个数。如果加上k的限制,我们其实就是求满足 (子字符串的长度减去出现次数最多的字符个数)<=k 的最大子字符串长度即可,搞清了这一点,我们也就应该知道怎么用滑动窗口来解了吧。我们用一个变量 start 记录滑动窗口左边界,初始化为0,然后遍历字符串,每次累加出现字符的个数,然后更新出现最多字符的个数,然后我们判断当前滑动窗口是否满足之前说的那个条件,如果不满足,我们就把滑动窗口左边界向右移动一个,并注意去掉的字符要在 counts 里减一,直到满足条件,我们更新结果 res 即可。需要注意的是,当滑动窗口的左边界向右移动了后,窗口内的相同字母的最大个数貌似可能会改变啊,为啥这里不用更新 maxCnt 呢?这是个好问题,原因是此题让求的是最长的重复子串,maxCnt 相当于卡了一个窗口大小,我们并不希望窗口变小,虽然窗口在滑动,但是之前是出现过跟窗口大小相同的符合题意的子串,缩小窗口没有意义,并不会使结果 res 变大,所以我们才不更新 maxCnt 的,参见代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intcharacterReplacement(string s, int k){ int res = 0, maxCnt = 0, start = 0; vector<int> counts(26, 0); for (int i = 0; i < s.size(); ++i) { maxCnt = max(maxCnt, ++counts[s[i] - 'A']); while (i - start + 1 - maxCnt > k) { --counts[s[start] - 'A']; ++start; } res = max(res, i - start + 1); } return res; } };
Leetcode427. Construct Quad Tree
Given a n * n matrix grid of 0’s and 1’s only. We want to represent the grid with a Quad-Tree.
Return the root of the Quad-Tree representing the grid.
Notice that you can assign the value of a node to True or False when isLeaf is False, and both are accepted in the answer.
A Quad-Tree is a tree data structure in which each internal node has exactly four children. Besides, each node has two attributes:
val: True if the node represents a grid of 1’s or False if the node represents a grid of 0’s.
isLeaf: True if the node is leaf node on the tree or False if the node has the four children.
1 2 3 4 5 6 7 8
classNode { public boolean val; public boolean isLeaf; public Node topLeft; public Node topRight; public Node bottomLeft; public Node bottomRight; }
We can construct a Quad-Tree from a two-dimensional area using the following steps:
If the current grid has the same value (i.e all 1’s or all 0’s) set isLeaf True and set val to the value of the grid and set the four children to Null and stop.
Example 1:
1 2 3 4 5 6
Input: grid = [[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0]] Output: [[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]] Explanation: All values in the grid are not the same. We divide the grid into four sub-grids. The topLeft, bottomLeft and bottomRight each has the same value. The topRight have different values so we divide it into 4 sub-grids where each has the same value. Explanation is shown in the photo below:
classSolution { public: vector<vector<int>> levelOrder(Node* root) { if (!root) return {}; vector<vector<int>> res; queue<Node*> q{{root}}; while (!q.empty()) { vector<int> out; for (int i = q.size(); i > 0; --i) { auto t = q.front(); q.pop(); out.push_back(t->val); if (!t->children.empty()) { for (auto a : t->children) q.push(a); } } res.push_back(out); } return res; } };
Leetcode430. Flatten a Multilevel Doubly Linked List
You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.
Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.
Example 1:
1 2
Input: head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12] Output: [1,2,3,7,8,11,12,9,10,4,5,6]
Explanation:
The multilevel linked list in the input is as follows:
Example 2:
1 2
Input: head = [1,2,null,3] Output: [1,3,2]
Explanation:
1 2 3 4 5
The input multilevel linked list is as follows:
1---2---NULL | 3---NULL
这道题给了一个多层的双向链表,让我们压平成为一层的双向链表,题目中给了形象的图例,不难理解题意。根据题目中给的例子,我们可以看出如果某个结点有下一层双向链表,那么下一层双向链表中的结点就要先加入进去,如果下一层链表中某个结点还有下一层,那么还是优先加入下一层的结点,整个加入的机制是DFS的,就是有岔路先走岔路,走到没路了后再返回,这就是深度优先遍历的机制。好,那么既然是DFS,肯定优先考虑递归啦。方法有了,再来看具体怎么递归。由于给定的多层链表本身就是双向的,所以我们只需要把下一层的结点移到第一层即可,那么没有子结点的结点就保持原状,不作处理。只有对于那些有子结点的,我们需要做一些处理,由于子结点链接的双向链表要加到后面,所以当前结点之后要断开,再断开之前,我们用变量 next 指向下一个链表,然后对子结点调用递归函数,我们 suppose 返回的结点已经压平了,那么就只有一层,就相当于要把这一层的结点加到断开的地方,所以需要知道这层的最后一个结点的位置,我们用一个变量 last,来遍历到压平的这一层的末结点。现在就可以开始链接了,首先把子结点链到 cur 的 next,然后把反向指针 prev 也链上。此时 cur 的子结点 child 可以清空,然后压平的这一层的末节点 last 链上之前保存的 next 结点,如果 next 非空,那么链上反向结点 prev。这些操作完成后,我们就已经将压平的这一层完整的加入了之前层断开的地方,继续在之前层往下遍历即可,参见代码如下:
classSolution { public: Node* flatten(Node* head){ Node *cur = head; while (cur) { if (cur->child) { Node *next = cur->next; Node *last = cur->child; while (last->next) last = last->next; cur->next = cur->child; cur->next->prev = cur; cur->child = NULL; last->next = next; if (next) next->prev = last; } cur = cur->next; } return head; } };
Leetcode433. Minimum Genetic Mutation
A gene string can be represented by an 8-character long string, with choices from ‘A’, ‘C’, ‘G’, and ‘T’.
Suppose we need to investigate a mutation from a gene string start to a gene string end where one mutation is defined as one single character changed in the gene string.
For example, “AACCGGTT” —> “AACCGGTA” is one mutation. There is also a gene bank bank that records all the valid gene mutations. A gene must be in bank to make it a valid gene string.
Given the two gene strings start and end and the gene bank bank, return the minimum number of mutations needed to mutate from start to end. If there is no such a mutation, return -1.
Note that the starting point is assumed to be valid, so it might not be included in the bank.
Example 1:
1 2
Input: start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"] Output: 1
Example 2:
1 2
Input: start = "AACCGGTT", end = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGGTA"] Output: 2
Example 3:
1 2
Input: start = "AAAAACCC", end = "AACCCCCC", bank = ["AAAACCCC","AAACCCCC","AACCCCCC"] Output: 3
classSolution { public: intminMutation(string start, string end, vector<string>& bank){ if (bank.empty()) return-1; bank.push_back(start); int res = 0, len = bank.size(); queue<int> q; vector<vector<int> > dist(len, vector<int>(len, 0)); vector<int> visited(len, 0); for (int i = 0; i < len; i ++) for (int j = i+1; j < len; j ++) dist[i][j] = cal_dist(bank[i], bank[j]); q.push(len-1); while(!q.empty()) { res ++; int size = q.size(); for (int i = 0; i < size; i ++) { int t = q.front(); q.pop(); visited[t] = true; for (int j = 0; j < len; j ++) { if ((dist[t][j] != 1 && dist[j][t] != 1) || visited[j]) continue; q.push(j); if (bank[j] == end) return res; } } } return-1; } intcal_dist(string a, string b){ int cnt = 0, len = a.length(); for (int i = 0; i < len; i ++) if (a[i] != b[i]) cnt ++; return cnt; } };
Leetcode434. Number of Segments in a String
Count the number of segments in a string, where a segment is defined to be a contiguous sequence of non-space characters.
Please note that the string does not contain any non-printable characters.
Example:
1 2
Input: "Hello, my name is John" Output: 5
判断一个句子中有几个段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: intcountSegments(string s){ int res = 0; if(s == "" || s == " ") return0; for(int i = 0; i < s.length(); i ++) { if(s[i] != ' ') res ++; while(i < s.length() && s[i] != ' ') i ++; } return res; } };
有一种简单做法:
1 2 3 4 5 6 7 8
intcountSegments(string s){ stringstream ss(s); string word; int count = 0; while (ss >> word) count++; return count; }
Leetcode435. Non-overlapping Intervals
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Example 1:
1 2 3
Input: intervals = [[1,2],[2,3],[3,4],[1,3]] Output: 1 Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.
Example 2:
1 2 3
Input: intervals = [[1,2],[1,2],[1,2]] Output: 2 Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.
这道题给了我们一堆区间,让求需要至少移除多少个区间才能使剩下的区间没有重叠,那么首先要给区间排序,根据每个区间的 start 来做升序排序,然后开始要查找重叠区间,判断方法是看如果前一个区间的 end 大于后一个区间的 start,那么一定是重复区间,此时结果 res 自增1,我们需要删除一个,那么此时究竟该删哪一个呢,为了保证总体去掉的区间数最小,我们去掉那个 end 值较大的区间,而在代码中,我们并没有真正的删掉某一个区间,而是用一个变量 last 指向上一个需要比较的区间,我们将 last 指向 end 值较小的那个区间;如果两个区间没有重叠,那么此时 last 指向当前区间,继续进行下一次遍历,参见代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: interaseOverlapIntervals(vector<vector<int>>& intervals){ int res = 0, begin = 0, end = 0; sort(intervals.begin(), intervals.end()); begin = intervals[0][0], end = intervals[0][1]; for (int i = 1; i < intervals.size(); i ++) { if (end > intervals[i][0]) { res ++; if (end > intervals[i][1]) end = intervals[i][1]; } else end = intervals[i][1]; } return res; } };
Leetcode436. Find Right Interval
You are given an array of intervals, where intervals[i] = [starti, endi] and each starti is unique.
The right interval for an interval i is an interval j such that startj >= endi and startj is minimized.
Return an array of right interval indices for each interval i. If no right interval exists for interval i, then put -1 at index i.
Example 1:
1 2 3
Input: intervals = [[1,2]] Output: [-1] Explanation: There is only one interval in the collection, so it outputs -1.
Example 2:
1 2 3 4 5
Input: intervals = [[3,4],[2,3],[1,2]] Output: [-1,0,1] Explanation: There is no right interval for [3,4]. The right interval for [2,3] is [3,4] since start0 = 3 is the smallest start that is >= end1 = 3. The right interval for [1,2] is [2,3] since start1 = 2 is the smallest start that is >= end2 = 2.
Example 3:
1 2 3 4
Input: intervals = [[1,4],[2,3],[3,4]] Output: [-1,2,-1] Explanation: There is no right interval for [1,4] and [3,4]. The right interval for [2,3] is [3,4] since start2 = 3 is the smallest start that is >= end1 = 3.
这道题给了我们一堆区间,让我们找每个区间的最近右区间,要保证右区间的 start 要大于等于当前区间的 end,由于区间的顺序不能变,所以我们不能给区间排序,我们需要建立区间的 start 和该区间位置之间的映射,由于题目中限定了每个区间的 start 都不同,所以不用担心一对多的情况出现。然后我们把所有的区间的 start 都放到一个数组中,并对这个数组进行降序排序,那么 start 值大的就在数组前面。然后我们遍历区间集合,对于每个区间,我们在数组中找第一个小于当前区间的 end 值的位置,如果数组中第一个数就小于当前区间的 end,那么说明该区间不存在右区间,结果 res 中加入-1;如果找到了第一个小于当前区间 end 的位置,那么往前推一个就是第一个大于等于当前区间 end 的 start,我们在 HashMap 中找到该区间的坐标加入结果 res 中即可,参见代码如下:(下边改进为二分搜索,速度快了十几倍)
classSolution { public: vector<int> findRightInterval(vector<vector<int>>& intervals){ unordered_map<int, int> map; vector<int> start; for (int i = 0; i < intervals.size(); i ++) { map[intervals[i][0]] = i; start.push_back(intervals[i][0]); } sort(start.begin(), start.end()); vector<int> ress; int len = intervals.size(); for (int i = 0; i < len; i ++) { int low = 0, high = len-1, mid; int best = -1; while(low <= high) { mid = low + (high-low) / 2; if (start[mid] < intervals[i][1]) low = mid+1; else { best = map[start[mid]]; high = mid-1; } } ress.push_back(best); } return ress; } };
Leetcode437. Path Sum III
You are given a binary tree in which each node contains an integer value. Find the number of paths that sum to a given value. The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
1 2 3 4 5 6 7 8 9 10 11 12
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 10 / \ 5 -3 / \ \ 3 2 11 / \ \ 3 -2 1 Return 3. The paths that sum to 8 are: 1. 5 -> 3 2. 5 -> 2 -> 1 3. -3 -> 11
这道题让我们求二叉树的路径的和等于一个给定值,说明了这条路径不必要从根节点开始,可以是中间的任意一段,而且二叉树的节点值也是有正有负。那么可以用递归来做,相当于先序遍历二叉树,对于每一个节点都有记录了一条从根节点到当前节点到路径,同时用一个变量 curSum 记录路径节点总和,然后看 curSum 和 sum 是否相等,相等的话结果 res 加1,不等的话继续查看子路径和有没有满足题意的,做法就是每次去掉一个节点,看路径和是否等于给定值,注意最后必须留一个节点,不能全去掉了,因为如果全去掉了,路径之和为0,而如果给定值刚好为0的话就会有问题。
classSolution { public: int ans = 0; voiddfs(TreeNode* root, vector<TreeNode*>& out, int sum, int cur){ if(root == NULL) return; cur += root->val; if (cur == sum) ++ans; out.push_back(root); int t = cur; for(int i = 0; i < out.size() - 1; i ++) { t = t - out[i]->val; if (t == sum) ++ans; } dfs(root->left, out, sum, cur); dfs(root->right, out, sum, cur); out.pop_back(); } intpathSum(TreeNode* root, int sum){ vector<TreeNode*> out; dfs(root, out, sum, 0); return ans; } };
Leetcode438. Find All Anagrams in a String
Given two strings s and p, return an array of all the start indices of p’s anagrams in s. You may return the answer in any order.
Example 1:
1 2 3 4 5
Input: s = "cbaebabacd", p = "abc" Output: [0,6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
1 2 3 4 5 6
Input: s = "abab", p = "ab" Output: [0,1,2] Explanation: The substring with start index = 0 is "ab", which is an anagram of "ab". The substring with start index = 1 is "ba", which is an anagram of "ab". The substring with start index = 2 is "ab", which is an anagram of "ab".
这道题给了我们两个字符串s和p,让在s中找字符串p的所有变位次的位置,所谓变位次就是字符种类个数均相同但是顺序可以不同的两个词,那么肯定首先就要统计字符串p中字符出现的次数,然后从s的开头开始,每次找p字符串长度个字符,来验证字符个数是否相同,如果不相同出现了直接 break,如果一直都相同了,则将起始位置加入结果 res 中,参见代码如下:(不用unordered_map而是用vector会更快!)
classSolution { public: vector<int> findAnagrams(string s, string p){ unordered_map<char, int> map, maps; vector<int> res; int lens = s.length(), lenp = p.length(); for (int i = 0; i < 26; i ++) { map['a' + i] = 0; maps['a' + i] = 0; } for (int i = 0; i < lenp; i ++) { map[p[i]] ++; } if (lenp > lens) return {}; for (int i = 0; i < lenp-1; i ++) maps[s[i]] ++; for (int i = lenp-1; i < lens; i ++) { maps[s[i]] ++; int j = 0; for(j = 0; j < 26; j ++) if (maps['a' + j] != map['a' + j]) break; if (j == 26) res.push_back(i-lenp+1); maps[s[i-lenp+1]] --; } return res; } };
Leetcode441. Arranging Coins
You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins.
Given n, find the total number of full staircase rows that can be formed. n is a non-negative integer and fits within the range of a 32-bit signed integer.
Example 1:
1 2 3 4 5 6 7
n = 5 The coins can form the following rows: ¤ ¤ ¤ ¤ ¤
Because the 3rd row is incomplete, we return 2.
Example 2:
1 2 3 4 5 6 7 8 9
n = 8
The coins can form the following rows: ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤
Given an integer array nums of length n where all the integers of nums are in the range [1, n] and each integer appears once or twice, return an array of all the integers that appears twice.
You must write an algorithm that runs in O(n) time and uses only constant extra space.
Example 1:
1 2
Input: nums = [4,3,2,7,8,2,3,1] Output: [2,3]
Example 2:
1 2
Input: nums = [1,1,2] Output: [1]
Example 3:
1 2
Input: nums = [1] Output: []
这类问题的一个重要条件就是1 ≤ a[i] ≤ n (n = size of array),不然很难在O(1)空间和O(n)时间内完成。首先来看一种正负替换的方法,这类问题的核心是就是找nums[i]和nums[nums[i] - 1]的关系,我们的做法是,对于每个nums[i],我们将其对应的nums[nums[i] - 1]取相反数,如果其已经是负数了,说明之前存在过,我们将其加入结果res中即可,参见代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
classSolution { public: vector<int> findDuplicates(vector<int>& nums){ vector<int> res; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) - 1; if (nums[idx] < 0) res.push_back(idx + 1); nums[idx] = -nums[idx]; } return res; } };
本题使用Set的数据结构对数组进行遍历,找到出现两次的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: vector<int> findDuplicates(vector<int>& nums){ vector<int> res; set<int> s; unordered_map<int, bool> flags; for (int n : nums) { if (!s.count(n)) s.insert(n); else res.push_back(n); } return res; } };
Leetcode443. String Compression
Given an array of characters, compress it in-place. The length after compression must always be smaller than or equal to the original array. Every element of the array should be a character (not int) of length 1. After you are done modifying the input array in-place, return the new length of the array.
Follow up: Could you solve it using only O(1) extra space?
Example 1:
1 2 3 4 5 6 7 8
Input: ["a","a","b","b","c","c","c"]
Output: Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"]
Explanation: "aa" is replaced by "a2". "bb" is replaced by "b2". "ccc" is replaced by "c3".
Example 2:
1 2 3 4 5 6 7 8
Input: ["a"]
Output: Return 1, and the first 1 characters of the input array should be: ["a"]
Output: Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"].
Explanation: Since the character "a" does not repeat, it is not compressed. "bbbbbbbbbbbb" is replaced by "b12". Notice each digit has it's own entry in the array.
classSolution { public: intcompress(vector<char>& chars){ int ans = 0; char c = chars[0], cc = c; int cur = 1, pointer = 0; string nums; for(int i = 1; i < chars.size(); i ++) { if(chars[i] != c) { cout << c << " " << cur << endl; chars[pointer++] = c; nums = to_string(cur); if(cur == 1) { c = chars[i]; continue; } for(int i = 0; i < nums.length(); i ++) { chars[pointer++] = nums[i]; } cur = 1; c = chars[i]; } else cur ++; } chars[pointer++] = c; if(cur == 1) { return pointer; } nums = to_string(cur); for(int i = 0; i < nums.length(); i ++) { chars[pointer++] = nums[i]; } return pointer; } };
Leetcode445. Add Two Numbers II
You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
classSolution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2){ stack<int> s1, s2; ListNode *head = l1; while(head) { s1.push(head->val); head = head->next; } head = l2; while(head) { s2.push(head->val); head = head->next; } head = NULL; int sum = 0; while(!s1.empty() || !s2.empty()) { if (!s1.empty()) { sum += s1.top(); s1.pop(); } if (!s2.empty()) { sum += s2.top(); s2.pop(); } head = newListNode(sum%10, head); sum /= 10; } if (sum > 0) head = newListNode(sum, head); return head; } };
Leetcode447. Number of Boomerangs
Given n points in the plane that are all pairwise distinct, a “boomerang” is a tuple of points (i, j, k) such that the distance between i and j equals the distance between i and k (the order of the tuple matters).
Find the number of boomerangs. You may assume that n will be at most 500 and coordinates of points are all in the range [-10000, 10000] (inclusive).
Example:
1 2 3
Input: [[0,0],[1,0],[2,0]] Output: 2 Explanation: The two boomerangs are [[1,0],[0,0],[2,0]] and [[1,0],[2,0],[0,0]]
给定 n 个两两各不相同的平面上的点,一个 “回旋镖” 是一个元组(tuple)的点(i,j,k),并且 i 和 j 的距离等于 i 和 k之间的距离(考虑顺序)。找出回旋镖的个数。你可以假设 n 不大于500,点的坐标范围在[-10000, 10000](包括边界)。
Leetcode448. Find All Numbers Disappeared in an Array
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
classSolution { public: vector<int> findDisappearedNumbers(vector<int>& nums){ vector<int> res; for (int i = 0; i < nums.size(); ++i) { if (nums[i] != nums[nums[i] - 1]) { swap(nums[i], nums[nums[i] - 1]); --i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] != i + 1) { res.push_back(i + 1); } } return res; } };
Leetcode449. Serialize and Deserialize BST
Serialization is converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary search tree. There is no restriction on how your serialization/deserialization algorithm should work. You need to ensure that a binary search tree can be serialized to a string, and this string can be deserialized to the original tree structure.
The encoded string should be as compact as possible.
Example 1:
1 2
Input: root = [2,1,3] Output: [2,1,3]
Example 2:
1 2
Input: root = [] Output: []
Constraints:
The number of nodes in the tree is in the range [0, 104].
0 <= Node.val <= 104
The input tree is guaranteed to be a binary search tree.
// Encodes a tree to a single string. string serialize(TreeNode* root){ string res = ""; if (root == NULL) return""; queue<TreeNode*> q; q.push(root); while (!q.empty()) { TreeNode* temp = q.front(); q.pop(); if (temp) { res += to_string(temp->val) + " "; q.push(temp->left); q.push(temp->right); } else res += "# "; } return res; }
// Decodes your encoded data to tree. TreeNode* deserialize(string data){ if (data == "") returnNULL; int pos = 0; TreeNode *root = newTreeNode(get_num(data, pos)); pos ++; queue<TreeNode*> q; q.push(root); while(!q.empty()) { TreeNode* temp = q.front(); q.pop(); if (data[pos] == '#') { temp->left = NULL; pos ++; } else { temp->left = newTreeNode(get_num(data, pos)); q.push(temp->left); } pos ++; if (data[pos] == '#') { temp->right = NULL; pos ++; } else { temp->right = newTreeNode(get_num(data, pos)); q.push(temp->right); } pos ++; } return root; } intget_num(string data, int& pos){ int res = 0; while(data[pos] != ' ') res = res * 10 + data[pos++] - '0'; return res; } };
// Encodes a tree to a single string. string serialize(TreeNode* root){ if (!root) return""; ostringstream os; queue<TreeNode*> q; q.push(root); while (!q.empty()) { TreeNode *t = q.front(); q.pop(); if (t) { os << t->val << " "; q.push(t->left); q.push(t->right); } else { os << "# "; } } return os.str(); }
// Decodes your encoded data to tree. TreeNode* deserialize(string data){ if (data.empty()) returnNULL; istringstream is(data); queue<TreeNode*> q; string val = ""; is >> val; TreeNode *res = newTreeNode(stoi(val)), *cur = res; q.push(cur); while (!q.empty()) { TreeNode *t = q.front(); q.pop(); if (!(is >> val)) break; if (val != "#") { cur = newTreeNode(stoi(val)); q.push(cur); t->left = cur; } if (!(is >> val)) break; if (val != "#") { cur = newTreeNode(stoi(val)); q.push(cur); t->right = cur; } } return res; } };
Leetcode450. Delete Node in a BST
Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.
Basically, the deletion can be divided into two stages:
Search for a node to remove. If the node is found, delete the node. Follow up: Can you solve it with time complexity O(height of tree)?
Example 1:
1 2 3 4 5
Input: root = [5,3,6,2,4,null,7], key = 3 Output: [5,4,6,2,null,null,7] Explanation: Given key to delete is 3. So we find the node with value 3 and delete it. One valid answer is [5,4,6,2,null,null,7], shown in the above BST. Please notice that another valid answer is [5,2,6,null,4,null,7] and it's also accepted.
Given a string s, sort it in decreasing order based on the frequency of characters, and return the sorted string.
Example 1:
1 2 3 4
Input: s = "tree" Output: "eert" Explanation: 'e' appears twice while 'r' and 't' both appear once. So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
Example 2:
1 2 3 4
Input: s = "cccaaa" Output: "aaaccc" Explanation: Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer. Note that "cacaca" is incorrect, as the same characters must be together.
Example 3:
1 2 3 4
Input: s = "Aabb" Output: "bbAa" Explanation: "bbaA" is also a valid answer, but "Aabb" is incorrect. Note that 'A' and 'a' are treated as two different characters.
竟然还要区分大小写,还要排序,那map等结构就不能用了,直接用数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: staticboolcomp(pair<char, int>& a, pair<char, int>& b){ return a.second > b.second || a.second == b.second && a.first < b.first; } string frequencySort(string s){ int count[256] = {0}; for (char c : s) count[c] ++; sort(s.begin(), s.end(), [&](char a, char b){ return count[a] > count[b] || count[a] == count[b] && a < b; }); return s; } };
Leetcode452. Minimum Number of Arrows to Burst Balloons
There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided input is the start and end coordinates of the horizontal diameter. Since it’s horizontal, y-coordinates don’t matter and hence the x-coordinates of start and end of the diameter suffice. Start is always smaller than end. There will be at most 104 balloons.
An arrow can be shot up exactly vertically from different points along the x-axis. A balloon with xstart and xend bursts by an arrow shot at x if xstart ≤ x ≤ xend. There is no limit to the number of arrows that can be shot. An arrow once shot keeps travelling up infinitely. The problem is to find the minimum number of arrows that must be shot to burst all balloons.
Example 1:
1 2 3
Input: points = [[10,16],[2,8],[1,6],[7,12]] Output: 2 Explanation: One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons).
classSolution { public: staticboolcomp(vector<int>& a, vector<int>& b){ return a[0] < b[0]; } intfindMinArrowShots(vector<vector<int>>& points){ sort(points.begin(), points.end(), comp); int res = 1, end = points[0][1]; for (int i = 1; i < points.size(); i ++) { if (end >= points[i][0]) end = min(end, points[i][1]); else { res ++; end = points[i][1]; } } return res; } };
Leetcode453. Minimum Moves to Equal Array Elements
Given a non-empty integer array of size n, find the minimum number of moves required to make all array elements equal, where a move is incrementing n - 1 elements by 1.
Example:
1 2 3 4 5 6 7 8 9
Input: [1,2,3]
Output: 3
Explanation: Only three moves are needed (remember each move increments two elements): [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
classSolution { public: intminMoves(vector<int>& nums){ int minn = INT_MAX, res = 0; for(int i : nums) minn = min(minn, i); for(int i : nums) res += (i - minn); return res; } };
Leetcode454. 4Sum II
Given four integer arrays nums1, nums2, nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that:
classSolution { public: intfourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) { map<int, int> index1,index2,index3; unordered_map<int,int> index4; for (size_t i = 0; i < nums4.size(); ++i) { index1[nums1[i]]++; index2[nums2[i]]++; index3[nums3[i]]++; index4[nums4[i]]++; } unordered_map<int, int> sums; for (auto && it3 : index3) for (auto && it4 : index4) sums[it3.first+it4.first] += it3.second*it4.second;
int count = 0; for (auto it1 = index1.begin(); it1 != index1.end(); ++it1) { for (auto it2 = index2.begin(); it2 != index2.end(); ++it2) { long t2 = (long) it1->first + (long) it2->first; int ct2 = it1->second * it2->second; auto pos = sums.find(-t2); if (pos == sums.end()) continue; count += pos->second*ct2; } } return count; } };
Leetcode455. Assign Cookies
Assume you are an awesome parent and want to give your children some cookies. But, you should give each child at most one cookie. Each child i has a greed factor gi, which is the minimum size of a cookie that the child will be content with; and each cookie j has a size sj. If sj >= gi, we can assign the cookie j to the child i, and the child i will be content. Your goal is to maximize the number of your content children and output the maximum number.
Note: You may assume the greed factor is always positive. You cannot assign more than one cookie to one child.
Example 1:
1 2 3 4 5
Input: [1,2,3], [1,1] Output: 1 Explanation: You have 3 children and 2 cookies. The greed factors of 3 children are 1, 2, 3. And even though you have 2 cookies, since their size is both 1, you could only make the child whose greed factor is 1 content. You need to output 1.
Example 2:
1 2 3 4 5
Input: [1,2], [1,2,3] Output: 2 Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2. You have 3 cookies and their sizes are big enough to gratify all of the children, You need to output 2.
Given an array of n integers nums, a 132 pattern is a subsequence of three integers nums[i], nums[j] and nums[k] such that i < j < k and nums[i] < nums[k] < nums[j].
Return true if there is a 132 pattern in nums, otherwise, return false.
Example 1:
1 2 3
Input: nums = [1,2,3,4] Output: false Explanation: There is no 132 pattern in the sequence.
Example 2:
1 2 3
Input: nums = [3,1,4,2] Output: true Explanation: There is a 132 pattern in the sequence: [1, 4, 2].
Example 3:
1 2 3
Input: nums = [-1,3,2,0] Output: true Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0].
思路是维护一个栈和一个变量 third,其中 third 就是第三个数字,也是 pattern 132 中的2,初始化为整型最小值,栈里面按顺序放所有大于 third 的数字,也是 pattern 132 中的3,那么在遍历的时候,如果当前数字小于 third,即 pattern 132 中的1找到了,直接返回 true 即可,因为已经找到了,注意应该从后往前遍历数组。如果当前数字大于栈顶元素,那么将栈顶数字取出,赋值给 third,然后将该数字压入栈,这样保证了栈里的元素仍然都是大于 third 的,想要的顺序依旧存在,进一步来说,栈里存放的都是可以维持坐标 second > third 的 second 值,其中的任何一个值都是大于当前的 third 值,如果有更大的值进来,那就等于形成了一个更优的 second > third 的这样一个组合,并且这时弹出的 third 值比以前的 third 值更大,为什么要保证 third 值更大,因为这样才可以更容易的满足当前的值 first 比 third 值小这个条件,举个例子来说吧,比如 [2, 4, 2, 3, 5],由于是从后往前遍历,所以后三个数都不会进入 while 循环,那么栈中的数字为 5, 3, 2(其中2为栈顶元素),此时 third 还是整型最小,那么当遍历到4的时候,终于4大于栈顶元素2了,那么 third 赋值为2,且2出栈。此时继续 while 循环,因为4还是大于新栈顶元素3,此时 third 赋值为3,且3出栈。现在栈顶元素是5,那么 while 循环结束,将4压入栈。下一个数字2,小于 third,则找到符合要求的序列 [2, 4, 3],参见代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: boolfind132pattern(vector<int>& nums){ int n = nums.size(), third = INT_MIN; stack<int> s; for (int i = n-1; i >= 0; i --) { if (nums[i] < third) returntrue; while(!s.empty() && nums[i] > s.top()) { third = s.top(); s.pop(); } s.push(nums[i]); } returnfalse; } };
Leetcode459. Repeated Substring Pattern
Given a non-empty string check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. You may assume the given string consists of lowercase English letters only and its length will not exceed 10000.
Example 1:
1 2 3
Input: "abab" Output: True Explanation: It's the substring "ab" twice.
Example 2:
1 2
Input: "aba" Output: False
Example 3:
1 2 3
Input: "abcabcabcabc" Output: True Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.)
The Hamming distance between two integers is the number of positions at which the corresponding bits are different. Given two integers x and y, calculate the Hamming distance.
Note: 0 ≤ x, y < 231.
Example:
1 2 3 4 5 6 7
Input: x = 1, y = 4 Output: 2
Explanation: 1 (0 0 0 1) 4 (0 1 0 0) ↑ ↑
The above arrows point to positions where the corresponding bits are different.
求两个数的海明距离,就是判断其二进制有多少不一样的位
1 2 3 4 5 6 7 8 9 10
classSolution { public: inthammingDistance(int x, int y){ int temp = x ^ y; int res=0; for(int i=temp;i>0;i=i>>1) if(i&1) res++; return res; } };
Leetcode462. Minimum Moves to Equal Array Elements II
Given an integer array nums of size n, return the minimum number of moves required to make all array elements equal.
In one move, you can increment or decrement an element of the array by 1.
Test cases are designed so that the answer will fit in a 32-bit integer.
Example 1:
1 2 3 4 5
Input: nums = [1,2,3] Output: 2 Explanation: Only two moves are needed (remember each move increments or decrements one element): [1,2,3] => [2,2,3] => [2,2,2]
classSolution { public: intminMoves2(vector<int>& nums){ int res = 0, n = nums.size(), mid = n / 2; nth_element(nums.begin(), nums.begin() + mid, nums.end()); for (int i = 0; i < n; ++i) { res += abs(nums[i] - nums[mid]); } return res; } };
Leetcode463. Island Perimeter
You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water.
Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells).
The island doesn’t have “lakes” (water inside that isn’t connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don’t exceed 100. Determine the perimeter of the island.
classSolution { public: intislandPerimeter(vector<vector<int>>& grid){ int m = grid.size(), n = grid[0].size(); int ans = 0, temp; for(int i = 0; i < m; i ++) { for(int j = 0; j < n; j ++) { if(!grid[i][j]) continue; temp = 0; if(j == 0 || (j > 0 && !grid[i][j-1])) temp ++; if(j == n-1 || (j < n-1 && !grid[i][j+1])) temp ++; if(i == 0 || (i > 0 && !grid[i-1][j])) temp ++; if(i == m-1 || (i < m-1 && !grid[i+1][j])) temp ++; ans += temp; } } return ans; } };
Leetcode464. Can I Win
In the “100 game,” two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.
Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.
You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.
Example
1 2 3 4 5 6 7 8 9 10 11 12 13
Input: maxChoosableInteger = 10 desiredTotal = 11
Output: false
Explanation: No matter which integer the first player choose, the first player will lose. The first player can choose an integer from 1 up to 10. If the first player choose 1, the second player can only choose integers from 2 up to 10. The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal. Same with other integers chosen by the first player, the second player will always win.
这道题给了我们一堆数字,然后两个人,每人每次选一个数字,看数字总数谁先到给定值,有点像之前那道 Nim Game,但是比那题难度大。我刚开始想肯定说用递归啊,结果写完发现 TLE 了,后来发现我们必须要优化效率,使用 HashMap 来记录已经计算过的结果。我们首先来看如果给定的数字范围大于等于目标值的话,直接返回 true。如果给定的数字总和小于目标值的话,说明谁也没法赢,返回 false。然后我们进入递归函数,首先我们查找当前情况是否在 HashMap 中存在,有的话直接返回即可。我们使用一个整型数按位来记录数组中的某个数字是否使用过,我们遍历所有数字,将该数字对应的 mask 算出来,如果其和 used 相与为0的话,说明该数字没有使用过,我们看如果此时的目标值小于等于当前数字,说明已经赢了,或者调用递归函数,如果返回 false,说明也是第一个人赢了。为啥呢,因为当前已经选过数字了,此时就该对第二个人调用递归函数,只有返回的结果是 false,我们才能赢,所以此时我们 true,并返回 true。如果遍历完所有数字,标记 false,并返回 false,参见代码如下:
classSolution { public: boolcanIWin(int maxChoosableInteger, int desiredTotal){ if (maxChoosableInteger >= desiredTotal) returntrue; if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) returnfalse; unordered_map<int, bool> m; returncanWin(maxChoosableInteger, desiredTotal, 0, m); } boolcanWin(int length, int total, int used, unordered_map<int, bool>& m){ if (m.count(used)) return m[used]; for (int i = 0; i < length; ++i) { int cur = (1 << i); if ((cur & used) == 0) { if (total <= i + 1 || !canWin(length, total - (i + 1), cur | used, m)) { m[used] = true; returntrue; } } } m[used] = false; returnfalse; } };
Leetcode467. Unique Substrings in Wraparound String
Consider the string s to be the infinite wraparound string of “abcdefghijklmnopqrstuvwxyz”, so s will look like this: “…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd….”.
Now we have another string p. Your job is to find out how many unique non-empty substrings of p are present in s. In particular, your input is the string p and you need to output the number of different non-empty substrings of p in the string s.
Note: p consists of only lowercase English letters and the size of p might be over 10000.
Example 1:
1 2 3 4
Input: "a" Output: 1
Explanation: Only the substring "a" of string "a" is in the string s.
Example 2:
1 2 3
Input: "cac" Output: 2 Explanation: There are two substrings "a", "c" of string "cac" in the string s.
Example 3:
1 2 3
Input: "zab" Output: 6 Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s.
classSolution { public: intfindSubstringInWraproundString(string p){ vector<int> cnt(26, 0); int len = 0; for (int i = 0; i < p.size(); ++i) { if (i > 0 && (p[i] == p[i - 1] + 1 || p[i - 1] - p[i] == 25)) { ++len; } else { len = 1; } cnt[p[i] - 'a'] = max(cnt[p[i] - 'a'], len); } returnaccumulate(cnt.begin(), cnt.end(), 0); } };
Leetcode468. Validate IP Address
Given a string IP, return “IPv4” if IP is a valid IPv4 address, “IPv6” if IP is a valid IPv6 address or “Neither” if IP is not a correct IP of any type.
A valid IPv4 address is an IP in the form “x1.x2.x3.x4” where 0 <= xi <= 255 and xi cannot contain leading zeros. For example, “192.168.1.1” and “192.168.1.0” are valid IPv4 addresses but “192.168.01.1”, while “192.168.1.00” and “192.168@1.1” are invalid IPv4 addresses.
A valid IPv6 address is an IP in the form “x1:x2:x3:x4:x5:x6:x7:x8” where:
1 <= xi.length <= 4
xi is a hexadecimal string which may contain digits, lower-case English letter (‘a’ to ‘f’) and upper-case English letters (‘A’ to ‘F’).
Leading zeros are allowed in xi.
For example, “2001:0db8:85a3:0000:0000:8a2e:0370:7334” and “2001:db8:85a3:0:0:8A2E:0370:7334” are valid IPv6 addresses, while “2001:0db8:85a3::8A2E:037j:7334” and “02001:0db8:85a3:0000:0000:8a2e:0370:7334” are invalid IPv6 addresses.
Example 1:
1 2 3
Input: IP = "172.16.254.1" Output: "IPv4" Explanation: This is a valid IPv4 address, return "IPv4".
Example 2:
1 2 3
Input: IP = "2001:0db8:85a3:0:0:8A2E:0370:7334" Output: "IPv6" Explanation: This is a valid IPv6 address, return "IPv6".
Example 3:
1 2 3
Input: IP = "256.256.256.256" Output: "Neither" Explanation: This is neither a IPv4 address nor a IPv6 address.
Example 4:
1 2
Input: IP = "2001:0db8:85a3:0:0:8A2E:0370:7334:" Output: "Neither"
classSolution { public: string validIPAddress(string const& IP) { bool isV6 = IP.find(':') != string::npos; if (isV6) { // Try to parse ipv6 int count = 0, segments = 0; auto isValidHex = [](char c) { returnisdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); }; int ptr = -1, size = IP.size(); while (ptr < size && segments < 8) { ++ptr; // skip leading ':' while (ptr < size && IP[ptr] != ':') if (isValidHex(IP[ptr])) ++ptr, ++count; else return"Neither"; if (count == 0 || count > 4) return"Neither"; else count = 0, ++segments; } if (ptr == IP.size() && segments == 8) return"IPv6"; else return"Neither"; } else { // Try to parse ipv4 int segments = 0, number = 0, count = 0; int ptr = -1, size = IP.size(); while (ptr < size && segments < 4) { ++ptr; // skip initial dot if (ptr < size && IP[ptr] == '0') { ++segments; ++ptr; if (ptr == size || IP[ptr] == '.') continue; else return"Neither"; }
while (ptr < size && IP[ptr] != '.') if (number < 250 && isdigit(IP[ptr])) { ++count; number *= 10; number += IP[ptr] - '0'; ++ptr; } else { return"Neither"; } if (1 <= count && count <= 4 && 1 <= number && number <= 255) { count = 0; number = 0; ++segments; } else { return"Neither"; } } if (ptr == IP.size() && segments == 4) return"IPv4"; else return"Neither"; } return"Neither"; } };
Leetcode470. Implement Rand10() Using Rand7()
Given the API rand7() that generates a uniform random integer in the range [1, 7], write a function rand10() that generates a uniform random integer in the range [1, 10]. You can only call the API rand7(), and you shouldn’t call any other API. Please do not use a language’s built-in random API.
Each test case will have one internal argument n, the number of times that your implemented function rand10() will be called while testing. Note that this is not an argument passed to rand10().
Follow up:
What is the expected value for the number of calls to rand7() function?
Could you minimize the number of calls to rand7()?
Given an array of strings words (without duplicates), return all the concatenated words in the given list of words.
A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array.
Example 1:
1 2 3 4 5
Input: words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; "ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat".
Example 2:
1 2
Input: words = ["cat","dog","catdog"] Output: ["catdog"]
classSolution { public: vector<string> findAllConcatenatedWordsInADict(vector<string>& words){ if (words.size() == 0) return {}; vector<string> res; unordered_set<string> dict(words.begin(), words.end()); for (int i = 0; i < words.size(); i ++) { dict.erase(words[i]); int len = words[i].size(); vector<bool> flag(len+1, false); flag[0] = true; for (int j = 0; j <= len; j ++) for (int k = 0; k < j; k ++) if (flag[k] && dict.count(words[i].substr(k, j-k))) { flag[j] = true; break; } if (flag[len]) res.push_back(words[i]); dict.insert(words[i]); } return res; } };
Leetcode473. Matchsticks to Square
Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match girl has, please find out a way you can make one square by using up all those matchsticks. You should not break any stick, but you can link them up, and each matchstick must be used exactly one time.
Your input will be several matchsticks the girl has, represented with their stick length. Your output will either be true or false, to represent whether you could make one square using all the matchsticks the little match girl has.
Example 1:
1 2 3
Input: [1,1,2,2,2] Output: true Explanation: You can form a square with length 2, one side of the square came two sticks with length 1.
Example 2:
1 2 3
Input: [3,3,3,3,4] Output: false Explanation: You cannot find a way to form a square with all the matchsticks.
Note:
The length sum of the given matchsticks is in the range of 0 to 10^9.
The length of the given matchstick array will not exceed 15.
classSolution { public: boolmakesquare(vector<int>& nums){ if (nums.empty() || nums.size() < 4) returnfalse; int sum = accumulate(nums.begin(), nums.end(), 0); if (sum % 4 != 0) returnfalse; vector<int> sums(4, 0); sort(nums.rbegin(), nums.rend()); returnhelper(nums, sums, 0, sum / 4); } boolhelper(vector<int>& nums, vector<int>& sums, int pos, int target){ if (pos >= nums.size()) { return sums[0] == target && sums[1] == target && sums[2] == target; } for (int i = 0; i < 4; ++i) { if (sums[i] + nums[pos] > target) continue; sums[i] += nums[pos]; if (helper(nums, sums, pos + 1, target)) returntrue; sums[i] -= nums[pos]; } returnfalse; } };
Leetcode474. Ones and Zeroes
In the computer world, use restricted resource you have to generate maximum benefit is what we always want to pursue.
For now, suppose you are a dominator of m 0s and n 1s respectively. On the other hand, there is an array with strings consisting of only 0s and 1s.
Now your task is to find the maximum number of strings that you can form with given m 0s and n 1s. Each 0 and 1 can be used at most once.
Note:
The given numbers of 0s and 1s will both not exceed 100
The size of given string array won’t exceed 600.
Example 1:
1 2 3
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 Output: 4 Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
Example 2:
1 2 3
Input: Array = {"10", "0", "1"}, m = 1, n = 1 Output: 2 Explanation: You could form "10", but then you'd have nothing left. Better form "0" and "1".
classSolution { public: intfindMaxForm(vector<string>& strs, int m, int n){ vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (string str : strs) { int zeros = 0, ones = 0; for (char c : str) (c == '0') ? ++zeros : ++ones; for (int i = m; i >= zeros; --i) { for (int j = n; j >= ones; --j) { dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1); } } } return dp[m][n]; } };
Leetcode475. Heaters
Winter is coming! Your first job during the contest is to design a standard heater with fixed warm radius to warm all the houses.
Now, you are given positions of houses and heaters on a horizontal line, find out minimum radius of heaters so that all houses could be covered by those heaters.
So, your input will be the positions of houses and heaters seperately, and your expected output will be the minimum radius standard of heaters.
Note:
Numbers of houses and heaters you are given are non-negative and will not exceed 25000.
Positions of houses and heaters you are given are non-negative and will not exceed 10^9.
As long as a house is in the heaters’ warm radius range, it can be warmed.
All the heaters follow your radius standard and the warm radius will the same.
Example 1:
1 2 3
Input: [1,2,3],[2] Output: 1 Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, then all the houses can be warmed.
Example 2:
1 2 3
Input: [1,2,3,4],[1,4] Output: 1 Explanation: The two heater was placed in the position 1 and 4. We need to use radius 1 standard, then all the houses can be warmed.
Given a positive integer num, output its complement number. The complement strategy is to flip the bits of its binary representation.
Example 1:
1 2 3
Input: num = 5 Output: 2 Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2.
Example 2:
1 2 3
Input: num = 1 Output: 0 Explanation: The binary representation of 1 is 1 (no leading zero bits), and its complement is 0. So you need to output 0.
The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
Given an integer array nums, return the sum of Hamming distances between all the pairs of the integers in nums.
Example 1:
1 2 3 4 5 6
Input: nums = [4,14,2] Output: 6 Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just showing the four bits relevant in this case). The answer will be: HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6.
classSolution { public: inttotalHammingDistance(vector<int>& nums){ int res = 0, n = nums.size(); for (int i = 0; i < 32; ++i) { int cnt = 0; for (int num : nums) { if (num & (1 << i)) ++cnt; } res += cnt * (n - cnt); } return res; } };
Leetcode478. Generate Random Point in a Circle
Given the radius and x-y positions of the center of a circle, write a function randPoint which generates a uniform random point in the circle.
Note:
input and output values are in floating-point.
radius and x-y position of the center of the circle is passed into the class constructor.
a point on the circumference of the circle is considered to be in the circle.
randPoint returns a size 2 array containing x-position and y-position of the random point, in that order.
The input is two lists: the subroutines called and their arguments. Solution’s constructor has three arguments, the radius, x-position of the center, and y-position of the center of the circle. randPoint has no arguments. Arguments are always wrapped with a list, even if there aren’t any.
classSolution { public: Solution(double radius, double x_center, double y_center) { r = radius; centerX = x_center; centerY = y_center; } vector<double> randPoint(){ while (true) { double x = (2 * (double)rand() / RAND_MAX - 1.0) * r; double y = (2 * (double)rand() / RAND_MAX - 1.0) * r; if (x * x + y * y <= r * r) return {centerX + x, centerY + y}; } }
private: double r, centerX, centerY; };
480. Sliding Window Median
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
Examples:
[2,3,4] , the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Given an array nums , there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Your job is to output the median array for each window in the original array.
classSolution { public: doublecalc(const multiset<int>& x, const multiset<int>& y, int k){ if (k & 1) return *x.rbegin(); return (1.0 * *x.rbegin() + *y.begin()) / 2.0; } voidremove(multiset<int>& x, multiset<int>& y, int val){ if (x.count(val)) { x.erase(x.find(val)); if (x.size() < y.size() && y.size()) { int v = *y.begin(); x.insert(v); y.erase(y.find(v)); } return; } y.erase(y.find(val)); if (x.size() - y.size() > 1) { int v = *x.rbegin(); x.erase(x.find(v)); y.insert(v); } } voidadd(multiset<int>& x, multiset<int>& y, int val){ if (x.empty() || val < *x.rbegin()) { x.insert(val); if (x.size() - y.size() > 1) { int v = *x.rbegin(); x.erase(x.find(v)); y.insert(v); } return; } y.insert(val); if (y.size() > x.size()) { int v = *y.begin(); y.erase(y.find(v)); x.insert(v); } } vector<double> medianSlidingWindow(vector<int>& a, int k){ int n = a.size(); multiset<int> x, y; // k 个元素 for (int i = 0 ; i < k; i ++) x.insert(a[i]); for (int i = 0; i < k/2; i ++) { int val = *x.rbegin(); x.erase(x.find(val)); y.insert(val); } vector<double> res; res.push_back(calc(x, y, k)); for (int l = 1, r = k; r < n; l ++, r ++) { // 先删除a[l-1],这是上一个窗口的最小元素 remove(x, y, a[l-1]); add(x, y, a[r]); res.push_back(calc(x, y, k)); } return res; } };
上面的方法用到了很多STL内置的函数,比如next,lower_bound啥的,下面我们来看一种不使用这些函数的解法。这种解法跟Find Median from Data Stream那题的解法很类似,都是维护了small和large两个堆,分别保存有序数组的左半段和右半段的数字,保持small的长度大于等于large的长度。我们开始遍历数组nums,如果i>=k,说明此时滑动窗口已经满k个了,再滑动就要删掉最左值了,我们分别在small和large中查找最左值,有的话就删掉。然后处理增加数字的情况(分两种情况:1.如果small的长度小于large的长度,再看如果large是空或者新加的数小于等于large的首元素,我们把此数加入small中。否则就把large的首元素移出并加入small中,然后把新数字加入large。2.如果small的长度大于large,再看如果新数字大于small的尾元素,那么新数字加入large中,否则就把small的尾元素移出并加入large中,把新数字加入small中)。最后我们再计算中位数并加入结果res中,根据k的奇偶性来分别处理,参见代码如下:
classSolution { public: intmagicalString(int n){ if (n <= 0) return0; if (n <= 3) return1; int res = 1, head = 2, tail = 3, num = 1; vector<int> v{1, 2, 2}; while (tail < n) { for (int i = 0; i < v[head]; ++i) { v.push_back(num); if (num == 1 && tail < n) ++res; ++tail; } num ^= 3; ++head; } return res; } }
Leetcode482. License Key Formatting
You are given a license key represented as a string S which consists only alphanumeric character and dashes. The string is separated into N+1 groups by N dashes.
Given a number K, we would want to reformat the strings such that each group contains exactly K characters, except for the first group which could be shorter than K, but still must contain at least one character. Furthermore, there must be a dash inserted between two groups and all lowercase letters should be converted to uppercase.
Given a non-empty string S and a number K, format the string according to the rules described above.
Example 1:
1 2 3 4
Input: S = "5F3Z-2e-9-w", K = 4 Output: "5F3Z-2E9W" Explanation: The string S has been split into two parts, each part has 4 characters. Note that the two extra dashes are not needed and can be removed.
Example 2:
1 2 3
Input: S = "2-5g-3-J", K = 2 Output: "2-5G-3J" Explanation: The string S has been split into three parts, each part has 2 characters except the first part as it could be shorter as mentioned above.
Note:
The length of string S will not exceed 12,000, and K is a positive integer.
String S consists only of alphanumerical characters (a-z and/or A-Z and/or 0-9) and dashes(-).
String S is non-empty.
繁琐的字符串拼接题。。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: string licenseKeyFormatting(string S, int K){ string tmp, res; for (int i = S.size() - 1; i >= 0; i--) { if(S[i] == '-') continue; tmp = (char) toupper(S[i]) + tmp; if(tmp.length() == K) { res.insert(0, "-" + tmp); tmp.clear(); } } if (tmp.empty() && !res.empty()) return res.substr(1); res = tmp + res; return res; } };
Leetcode485. Max Consecutive Ones
Given a binary array, find the maximum number of consecutive 1s in this array.
Example 1:
1 2 3 4
Input: [1,1,0,1,1,1] Output: 3 Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3.
计算最长的连续1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intfindMaxConsecutiveOnes(vector<int>& nums){ int res = 0, count = 0;; for(int i = 0; i < nums.size(); i ++) { if(nums[i] == 0) { res = count > res ? count : res; count = 0; } else count ++; } res = count > res ? count : res; return res; } };
Leetcode486. Predict the Winner
Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.
Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.
Example 1:
1 2 3 4 5 6
Input: [1, 5, 2] Output: False Explanation: Initially, player 1 can choose between 1 and 2. If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2). So, final score of player 1 is 1 + 2 = 3, and player 2 is 5. Hence, player 1 will never be the winner and you need to return False.
Example 2:
1 2 3 4
Input: [1, 5, 233, 7] Output: True Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233. Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.
Note:
1 <= length of the array <= 20.
Any scores in the given array are non-negative integers and will not exceed 10,000,000.
If the scores of both players are equal, then player 1 is still the winner.
Given an integer array nums, return all the different possible increasing subsequences of the given array with at least two elements. You may return the answer in any order.
The given array may contain duplicates, and two equal integers should also be considered a special case of increasing sequence.
classSolution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { set<vector<int>> res; vector<int> s; helper(nums, 0, s, res); returnvector(res.begin(), res.end()); } voidhelper(vector<int>& nums, int i, vector<int> s, set<vector<int>>& res){ if (s.size() > 1) res.insert(s); for (; i < nums.size(); i ++) { if (!s.empty() && s.back() > nums[i]) continue; s.push_back(nums[i]); helper(nums, i+1, s, res); s.pop_back(); } } };
Leetcode492. Construct the Rectangle
For a web developer, it is very important to know how to design a web page’s size. So, given a specific rectangular web page’s area, your job by now is to design a rectangular web page, whose length L and width W satisfy the following requirements:
The area of the rectangular web page you designed must equal to the given target area.
The width W should not be larger than the length L, which means L >= W.
The difference between length L and width W should be as small as possible.
You need to output the length L and the width W of the web page you designed in sequence. Example:
1 2 3 4
Input: 4 Output: [2, 2] Explanation: The target area is 4, and all the possible ways to construct it are [1,4], [2,2], [4,1]. But according to requirement 2, [1,4] is illegal; according to requirement 3, [4,1] is not optimal compared to [2,2]. So the length L is 2, and the width W is 2.
这道题给了我们一个数组,和一个目标值,让给数组中每个数字加上正号或负号,然后求和要和目标值相等,求有多少中不同的情况。那么对于这种求多种情况的问题,博主最想到的方法使用递归来做。从第一个数字,调用递归函数,在递归函数中,分别对目标值进行加上当前数字调用递归,和减去当前数字调用递归,这样会涵盖所有情况,并且当所有数字遍历完成后,若目标值为0了,则结果 res 自增1,参见代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: intfindTargetSumWays(vector<int>& nums, int S){ int res = 0; helper(nums, S, 0, res); return res; } voidhelper(vector<int>& nums, long S, int start, int& res){ if (start >= nums.size()) { if (S == 0) ++res; return; } helper(nums, S - nums[start], start + 1, res); helper(nums, S + nums[start], start + 1, res); } };
classSolution { public: intfindTargetSumWays(vector<int>& nums, int S){ vector<unordered_map<int, int>> memo(nums.size()); returnhelper(nums, S, 0, memo); } inthelper(vector<int>& nums, long sum, int start, vector<unordered_map<int, int>>& memo){ if (start == nums.size()) return sum == 0; if (memo[start].count(sum)) return memo[start][sum]; int cnt1 = helper(nums, sum - nums[start], start + 1, memo); int cnt2 = helper(nums, sum + nums[start], start + 1, memo); return memo[start][sum] = cnt1 + cnt2; } };
Leetcode495. Teemo Attacking
Our hero Teemo is attacking an enemy Ashe with poison attacks! When Teemo attacks Ashe, Ashe gets poisoned for a exactly duration seconds. More formally, an attack at second t will mean Ashe is poisoned during the inclusive time interval [t, t + duration - 1]. If Teemo attacks again before the poison effect ends, the timer for it is reset, and the poison effect will end duration seconds after the new attack.
You are given a non-decreasing integer array timeSeries, where timeSeries[i] denotes that Teemo attacks Ashe at second timeSeries[i], and an integer duration.
Return the total number of seconds that Ashe is poisoned.
Example 1:
1 2 3 4 5 6
Input: timeSeries = [1,4], duration = 2 Output: 4 Explanation: Teemo's attacks on Ashe go as follows: - At second 1, Teemo attacks, and Ashe is poisoned for seconds 1 and 2. - At second 4, Teemo attacks, and Ashe is poisoned for seconds 4 and 5. Ashe is poisoned for seconds 1, 2, 4, and 5, which is 4 seconds in total.
Example 2:
1 2 3 4 5 6
Input: timeSeries = [1,2], duration = 2 Output: 3 Explanation: Teemo's attacks on Ashe go as follows: - At second 1, Teemo attacks, and Ashe is poisoned for seconds 1 and 2. - At second 2 however, Teemo attacks again and resets the poison timer. Ashe is poisoned for seconds 2 and 3. Ashe is poisoned for seconds 1, 2, and 3, which is 3 seconds in total.
classSolution { public: intfindPoisonedDuration(vector<int>& timeSeries, int duration){ int last = -1, res = 0; for (int i = 0; i < timeSeries.size(); i ++) { if (timeSeries[i] <= last) { if (timeSeries[i] + duration > last) { res = res + (timeSeries[i] + duration - last -1); last = timeSeries[i] + duration - 1; } } else { res += duration; last = timeSeries[i] + duration - 1; } } return res; } };
Leetcode496. Next Greater Element I
You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of nums2. Find all the next greater numbers for nums1’s elements in the corresponding places of nums2.
The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, output -1 for this number.
Example 1:
1 2 3 4 5 6
Input: nums1 = [4,1,2], nums2 = [1,3,4,2]. Output: [-1,3,-1] Explanation: For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. For number 1 in the first array, the next greater number for it in the second array is 3. For number 2 in the first array, there is no next greater number for it in the second array, so output -1.
Example 2:
1 2 3 4 5
Input: nums1 = [2,4], nums2 = [1,2,3,4]. Output: [3,-1] Explanation: For number 2 in the first array, the next greater number for it in the second array is 3. For number 4 in the first array, there is no next greater number for it in the second array, so output -1.
classSolution { public: vector<int> findDiagonalOrder(vector<vector<int>>& mat){ if (mat.empty() || mat[0].empty()) return {}; int x = 0, y = 0, dir = 0; vector<int> res; vector<vector<int>> dirs{{-1,1}, {1,-1}}; int m = mat.size(), n = mat[0].size(), size = m*n; for (int i = 0; i < size; i ++) { res.push_back(mat[x][y]); x += dirs[dir][0]; y += dirs[dir][1]; if (x >= m) { x = m - 1; y += 2; dir = 1 - dir; } if (y >= n) { y = n - 1; x += 2; dir = 1 - dir; } if (x < 0) { x = 0; dir = 1 - dir; } if (y < 0) { y = 0; dir = 1 - dir; } } return res; } };
Leetcode500. Keyboard Row
Given a List of words, return the words that can be typed using letters of alphabet on only one row’s of American keyboard like the image below.