关于指针:c+内存泄漏与智能指针

您好,看到文章的您,如果您是c++初学者,这部分内容须要学习好数据结构中指针和栈的内容,否则可能会有肯定难度,只做理解即可什么是内存透露什么是内存透露,简略来说,就是 1.动静申请堆空间,用完后不偿还2。C++ 语言中没有垃圾回收的机制3.指针无法控制所指堆空间的生命周期例如上面的例子: #include<iostream>#include<string.h>using namespace std;class test{ int i;public: test(int i) { this->i = i; } int value() { return i; } ~test() { }};int main(){ for (int i = 0; i < 5; i++) { test* p = new test(i); cout << p->value() << endl; } return 0;}智能指针的利用从输入后果能够看出,指针被用于了大小比拟和运算,这显然不是咱们冀望的。于是有了智能指针,demo如下: #include <iostream>#include <string>using namespace std;class Test{ int i;public: Test(int i) { cout << "Test(int i)" << endl; this->i = i; } int value() { return i; } ~Test() { cout << "~Test()" << endl; }};class Pointer{ Test* mp;public: Pointer(Test* p = NULL) { mp = p; } Pointer(const Pointer& obj) { mp = obj.mp; const_cast<Pointer&>(obj).mp = NULL; } Pointer& operator = (const Pointer& obj) { if (this != &obj) { delete mp; mp = obj.mp; const_cast<Pointer&>(obj).mp = NULL; } return *this; } Test* operator -> () { return mp; } Test& operator * () { return *mp; } bool isNull() { return (mp == NULL); } ~Pointer() { delete mp; }};int main(){ Pointer p1 = new Test(0); cout << p1->value() << endl; Pointer p2 = p1; cout << p1.isNull() << endl; cout << p2->value() << endl; return 0;}这里次要解决的以下问题 ...

June 4, 2022 · 1 min · jiezi

关于指针:指针作为函数形参使最大最小数对调

输出10个整数,将其中最小的数与第一个数对换,把最大的数与最初一个数对换。写3个函数:①输出10个数;②进行解决;③输入10个数。所有函数的参数均用指针。 include <stdio.h>include <stdlib.h>void In(int *b); int Swap(int x, int y); void Out(int *b); int main() { int *b, x[10];b = x;In(b);int k = 0, K = 9;for (int j = 0;j<10;j++){ if (x[k]>x[j]) { k = j; }}for (int g = 0;g<10;g++){ if (x[K]<x[g]) { K = g; }}if (k != 0) Swap(&x[0], &x[k]);if (K != 9) Swap(&x[9], &x[K]);Out(b);return 0;} void In(int *b) { int i;for (int i = 0;i<10;i++){ scanf("%d", b + i);}} ...

October 17, 2021 · 1 min · jiezi

关于指针:const和一二级指针

一. 一级指针const int *p; //润饰*pint const *p; //润饰*pint *const p; //润饰pconst int *const p; //第一个润饰*p,第二个润饰pA://正确,const int* = int*int a = 10 ;const int* p= &a;B://正确 const int* = const int*const int a=10;const int* p=&a;C://正确 int* = int*int a = 10;int *const p =&a;D://谬误 int* = const int*int a = 10;const int *p=&a;int *q = p;二. 二级指针const int **p; //润饰**pint const **p; //润饰**pint *const *p; //润饰*pint **const p; //润饰pconst int *const*p; //第一个润饰**p,第二个润饰*pA. // 谬误,const int** = int**int a = 10;int *p = &a;const int **q=&pB.// 正确,int *const* = int**int a = 10;int *p=&a;int *const*q = &p;C.// 正确,int **= int**int a = 10;int *p=&a;int **const q = &p;D.// 谬误,int** = int*const*int a = 10;int *const p=&a;int **q = &p;E.// 谬误,int*const* = const int**int a = 10;const int *p=&a;int *const*q = &p;

April 16, 2021 · 1 min · jiezi

Golang研学在函数方法接口中用好指针类型

在大部分面向对象语言如C++、C#、Java,在函数传参数时除了基础值类型,对象是通过引用方式传递的。 然而,在Go语言中,除了map、slice和chan,所有类型(包括struct)都是值传递的。 那么,如何在函数外使用函数内处理后的变量呢?只能通过返回新变量吗? 不,可以使用指针 大部分面向对象语言都很少有用到指针的场景了,但是在Go语言中有大量的指针应用场景,要成为一名合格的Gopher,必须了解。 概念每一个变量都会分配一块内存,数据保存在内存中,内存有一个地址,就像门牌号,通过这个地址就可以找到里面存储的数据。 指针就是保存这个内存地址的变量。 在Go语言中,用&取得变量的地址 //为了说明类型,我采用了显性的变量定义方法,实际开发中更多的是用“:=”自动获取类型变量类型var mystr string = "Hello!"var mystrP *string = &mystrfmt.Println(mystrP)将以上代码敲入main函数中,go run,打印出的内容就是mystr的内存地址。mystrP就是mystr的指针变量。 用*取得指针变量指向的内存地址的值 在之前的代码的后面增加一句代码: fmt.Println(*mystrPointer)go run 运行后,可以看到打印出 mystr的值“Hello!” 符号*也用做定义指针类型的关键字。 例如: var p *int指针应用场景在其他OOP语言中,大多数情况是不需要花太多时间操作指针的,如Java、C#,对象的引用操作都已经交给了虚拟机和框架。而Go经常会用到指针。原因主要有3个: Go语言中除了map、slice、chan外,其他类型在函数参数中都是值传递Go语言不是面向对象的语言,很多时候实现结构体方法时需要用指针类型实现引用结构体对象指针也是一个类型,在实现接口interface时,结构体类型和其指针类型对接口的实现是不同的接下来就分别介绍一下,期间会穿插一些简单的代码片段,您可以创建一个Go文件输入代码,运行体验一下。 函数中传递指针参数Go语言都是值传递,例如: package mainimport "fmt"func main() { i := 0 f(i) fmt.Println(i)}func f(count int) { fmt.Println(count) count++}结果: 00i在执行前后没有变化 如果希望被函数调用后,i的值产生变化,f函数的参数就应该改为 *int 类型。如: func main() { i := 0 f(&i) fmt.Println(i)}func f(count *int) { fmt.Println(*count) (*count)++}f定义参数用 *int 替代 int,申明参数是一个int类型的指针类型调用函数时,不能直接传递int的变量i,而要传递用&取得i的地址f函数内,参数count现在是指针了,不能直接打印,需要用*取出这个指针指向的地址里保存的值count的取值+1.调用f函数,在主函数main里打印i。可以看到结果 ...

April 28, 2019 · 1 min · jiezi

【Go】string 也是引用类型

原文链接:https://blog.thinkeridea.com/…初学 Go 语言的朋友总会在传 []byte 和 string 之间有着很多纠结,实际上是没有了解 string 与 slice 的本质,而且读了一些程序源码,也发现很多与之相关的问题,下面类似的代码估计很多初学者都写过,也充分说明了作者当时内心的纠结:package mainimport “bytes"func xx(s []byte) []byte{ …. return s}func main(){ s := “xxx” s = string(xx([]byte(s))) s = string(bytes.Replace([]byte(s), []byte(“x”), []byte(”"), -1))}虽然这样的代码并不是来自真实的项目,但是确实有人这样设计,单从设计上看就很糟糕了,这样设计的原因很多人说:“slice 是引用类型,传递引用类型效率高呀”,主要原因不了解两者的本质,加上文档上说 Go 的引用类型有两种:slice 和 map ,这个在面试中也是经常遇到的吧。上面这个例子如果觉得有点基础和可爱,下面这个例子貌似并不那么容易说明其存在的问题了吧。package mainfunc xx(s *string) *string{ …. return s}func main(){ s := “xx” s = *xx(&s) ss :=[]*string{} ss = append(ss, &s)}指针效率高,我就用指针多好,可以减少内存分配呀,设计函数都接收指针变量,程序性能会有很大提升,在实际的项目中这种例子也不少见,我想通过这篇文档来帮助初学者走出误区,减少适得其反的优化技巧。slice 的定义在之前 “【Go】深入剖析slice和array” 一文中说了 slice 在内存中的存储模式,slice 本身包含一个指向底层数组的指针,一个 int 类型的长度和一个 int 类型的容量, 这就是 slice 的本质, []byte 本身也是一个 slice,只是底层数组存储的元素是 byte。下面这个图就是 slice 的在内存中的状态:看一下 reflect.SliceHeader 如何定义 slice 在内存中的结构吧:type SliceHeader struct { Data uintptr Len int Cap int}slice 是引用类型是 slice 本身会包含一个地址,在传递 slice 时只需要分配 SliceHeader 就好了, 而 SliceHeader 只包含了三个 int 类型,相当于传递一个 slice 就只需要拷贝 SliceHeader,而不用拷贝整个底层数组,所以才说 slice 是引用类型的。那么字符串呢,计算机中我们处理的大多数问题都和字符串有关,难道传递字符串真的需要那么高的成本,需要借助 slice 和指针来减少内存开销吗。string 的定义reflect 包里面也定义了一个 StringHeader 看一下吧:type StringHeader struct { Data uintptr Len int}字符串只包含了两个 int 类型的数据,其中一个是指针,一个是字符串的长度,从定义上来看 string 也是引用类型。借助 unsafe 来分析一下情况是不是这样吧:package mainimport ( “reflect” “unsafe” “github.com/davecgh/go-spew/spew”)func xx(s string) { sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) spew.Dump(sh)}func main() { s := “xx” sh := *(reflect.StringHeader)(unsafe.Pointer(&s)) spew.Dump(sh) xx(s) xx(s[:1]) xx(s[1:])}上面这段代码的输出如下:(reflect.StringHeader) { Data: (uintptr) 0x10f5ee0, Len: (int) 2}(reflect.StringHeader) { Data: (uintptr) 0x10f5ee0, Len: (int) 2}(reflect.StringHeader) { Data: (uintptr) 0x10f5ee0, Len: (int) 1}(reflect.StringHeader) { Data: (uintptr) 0x10f5ee1, Len: (int) 1}可以发现前三个输出的指针都是同一个地址,第四个的地址发生了一个字节的偏移,分析来看传递字符串确实没有分配新的内存,同时和 slice 一样即使传递字符串的子串也不会分配新的内存空间,而是指向原字符串的中的一个位置。这样说来把 string 转成 []byte 还浪费的一个 int 的空间呢,需要分配更多的内存,真是适得其反呀,而且类型转换会发生内存拷贝,从 string 转为 []byte 才是真的把 string 底层数据全部拷贝一遍呢,真是得不偿失呀。string 的两个小特性字符串还有两个小特性,针对字面量(就是直接写在程序中的字符串),会创建在只读空间上,并且被复用,看一下下面的一个小例子:package mainimport ( “reflect” “unsafe” “github.com/davecgh/go-spew/spew”)func main() { a := “xx” b := “xx” c := “xxx” spew.Dump((reflect.StringHeader)(unsafe.Pointer(&a))) spew.Dump((reflect.StringHeader)(unsafe.Pointer(&b))) spew.Dump((*reflect.StringHeader)(unsafe.Pointer(&c)))}从输出可以了解到,相同的字面量会被复用,但是子串是不会复用空间的,这就是编译器给我们带来的福利了,可以减少字面量字符串占用的内存空间。(reflect.StringHeader) { Data: (uintptr) 0x10f5ea0, Len: (int) 2}(reflect.StringHeader) { Data: (uintptr) 0x10f5ea0, Len: (int) 2}(reflect.StringHeader) { Data: (uintptr) 0x10f5f2e, Len: (int) 3}另一个小特性大家都知道,就是字符串是不能修改的,如果我们不希望调用函数修改我们的数据,最好传递字符串,高效有安全。不过有了 unsafe 这个黑魔法,字符串的这一个特性也就不那么可靠了。package mainimport ( “fmt” “reflect” “strings” “unsafe”)func main() { a := strings.Repeat(“x”, 10) fmt.Println(a) strHeader := *(*reflect.StringHeader)(unsafe.Pointer(&a)) sliceHeader := reflect.SliceHeader{ Data: strHeader.Data, Len: strHeader.Len, Cap: strHeader.Len, } b := ([]byte)(unsafe.Pointer(&sliceHeader)) b[1] = ‘a’ fmt.Println(a)}从输出里面居然发现字符串被修改了, 我们没有办法直接修改字符串,但是可以利用 slice 和 string 本身结构的特性,创建一个 slice 让它的指针指向 string 的指针位置,然后借助 unsafe 把这个 SliceHeader 转成 []byte 来修改字符串,字符串确实被修改了。xxxxxxxxxxxaxxxxxxxx看了上面的例子是不是开始担心把字符串传给其它函数真的不会更改吗?感觉很不放心的样子,难道使用任何函数都要了解它的内部实现吗,其实这种情况极少发生,还记得之前说的那个字符串特性吗,字面量字符串会放到只读空间中,这个很重要,可以保证不是任何函数想修改我们的字符串就可以修改的。package mainimport ( “reflect” “unsafe”)func main() { defer func() { recover() }() a := “xx” strHeader := *(*reflect.StringHeader)(unsafe.Pointer(&a)) sliceHeader := reflect.SliceHeader{ Data: strHeader.Data, Len: strHeader.Len, Cap: strHeader.Len, } b := ([]byte)(unsafe.Pointer(&sliceHeader)) b[1] = ‘a’}运行上面的代码发生了一个运行时不可修复的错误,就是这个特性其它函数不能确保输入字符串是否是字面量,也是不会恶意修改我们字符串的了。unexpected fault address 0x1095dd5fatal error: fault[signal SIGBUS: bus error code=0x2 addr=0x1095dd5 pc=0x106c804]goroutine 1 [running]:runtime.throw(0x1095fde, 0x5) /usr/local/go/src/runtime/panic.go:608 +0x72 fp=0xc000040700 sp=0xc0000406d0 pc=0x10248d2runtime.sigpanic() /usr/local/go/src/runtime/signal_unix.go:387 +0x2d7 fp=0xc000040750 sp=0xc000040700 pc=0x1037677main.main() /Users/qiyin/project/go/src/github.com/yumimobi/test/a.go:22 +0x84 fp=0xc000040798 sp=0xc000040750 pc=0x106c804runtime.main() /usr/local/go/src/runtime/proc.go:201 +0x207 fp=0xc0000407e0 sp=0xc000040798 pc=0x1026247runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc0000407e8 sp=0xc0000407e0 pc=0x104da51关于字符串转 []byte 在 go-extend 扩展包中有直接的实现,这种用法在 go-extend 内部方法实现中也有大量使用, 实际上因为原数据类型和处理数据的函数类型不一致,使用这种方法转换字符串和 []byte 可以极大的提升程序性能exbytes.ToString 零成本的把 []byte 转为 string。exstrings.UnsafeToBytes 零成本的把 []byte 转为 string。上面这两个函数用的好,可以极大的提升我们程序的性能,关于 exstrings.UnsafeToBytes 我们转换不确定是否是字面量的字符串时就需要确保调用的函数不会修改我们的数据,这往常在调用 bytes 里面的方法十分有效。传字符串和字符串指针的区别之前分析了传递 slice 并没有 string 高效,何况转换数据类型本身就会发生数据拷贝。那么在这篇文章的第二个例子,为什么说传递字符串指针也不好呢,要了解指针在底层就是一个 int 类型的数据,而我们字符串只是两个 int 而已,另外如果了解 GC 的话,GC 只处理堆上的数据,传递指针字符串会导致数据逃逸到堆上,阅读标准库的代码会有很多注释说明避免逃逸到堆上,这样会极大的增加 GC 的开销,GC 的成本可谓是很高的呀。疑惑这篇文章说 “传递 slice 并没有 string 高效”,为什么还会有 bytes 包的存在呢,其中很多函数的功能和 strings 包的功能一致,只是把 string 换成了 []byte, 既然传递 []byte 没有 string 效率好,这个包存在的意义是什么呢。我们想一下转换数据类型是会发生数据拷贝,这个成本可是大的多呀,如果我们数据本身就是 []byte 类型,使用 strings 包就需要转换数据类型了。另外我们对比两个函数来看下一下即使传递 []byte 没有 string 效率好,但是标准库实现上却会导致两个函数有很大的性能差异的。strings.Repeat 函数:func Repeat(s string, count int) string { // Since we cannot return an error on overflow, // we should panic if the repeat will generate // an overflow. // See Issue golang.org/issue/16237 if count < 0 { panic(“strings: negative Repeat count”) } else if count > 0 && len(s)*count/count != len(s) { panic(“strings: Repeat count causes overflow”) } b := make([]byte, len(s)*count) bp := copy(b, s) for bp < len(b) { copy(b[bp:], b[:bp]) bp *= 2 } return string(b)}bytes.Repeat 函数:func Repeat(b []byte, count int) []byte { // Since we cannot return an error on overflow, // we should panic if the repeat will generate // an overflow. // See Issue golang.org/issue/16237. if count < 0 { panic(“bytes: negative Repeat count”) } else if count > 0 && len(b)*count/count != len(b) { panic(“bytes: Repeat count causes overflow”) } nb := make([]byte, len(b)*count) bp := copy(nb, b) for bp < len(nb) { copy(nb[bp:], nb[:bp]) bp *= 2 } return nb}上面两个函数的实现非常相似,除了类型不同 strings 包在处理完数据发生了一次类型转换,使用 bytes 只有一次内存分配,而 strings 是两次。我们可以借助 exbytes.ToString 函数把 bytes.Repeat 的返回没有任何成本的转换会我们需要的字符串,如果我们输入也是一个字符串的话,还可以借助 exstrings.UnsafeToBytes 来转换输入的数据类型。例如:s := exbytes.ToString(bytes.Repeat(exstrings.UnsafeToBytes(“x”), 10))不过这样写有点太麻烦了,实际上 exstrings 包里面正在修改 strings 里面一些类似函数的问题,所有的实现基本和标准库一致,只是把其中类型转换的部分用 exbytes.ToString 优化了一下,可以提升性能,也能提升开发效率。exstrings.UnsafeRepeat 函数:func UnsafeRepeat(s string, count int) string { // Since we cannot return an error on overflow, // we should panic if the repeat will generate // an overflow. // See Issue golang.org/issue/16237 if count < 0 { panic(“strings: negative Repeat count”) } else if count > 0 && len(s)*count/count != len(s) { panic(“strings: Repeat count causes overflow”) } b := make([]byte, len(s)*count) bp := copy(b, s) for bp < len(b) { copy(b[bp:], b[:bp]) bp *= 2 } return exbytes.ToString(b)}如果用上面的函数只需要下面这样写就可以了:s:=exstrings.UnsafeRepeat(“x”, 10)go-extend 里面还收录了很多实用的方法,大家也可以多关注。总结千万不要为了使用 []byte 来优化 string 传递,类型转换成本很高,且 slice 本身也比 string 更大一些。程序中是使用 string 还是 []byte 需要根据数据来源和处理数据的函数来决定,一定要减少类型转换。关于使用 strings 还是 bytes 包的问题,主要关注点是数据原始类型以及想获得的数据类型来选择。减少使用字符串指针来优化字符串,这会增加 GC 的开销,具体可以参考 大堆中避免大量的GC开销 一文。转载:本文作者: 戚银(thinkeridea)本文链接: https://blog.thinkeridea.com/201902/go/string_ye_shi_yin_yong_lei_xing.html版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处! ...

February 24, 2019 · 4 min · jiezi

Go:指针能优化性能吗?【译】

趁着元旦休假+春节,尝试把2018年期间让我受益的一些文章、问答,翻译一下。欢迎指正、讨论,希望对你也有所帮助。原文链接:Go: Are pointers a performance optimization?以下,开始正文过去几周时间,我回答了许多关于使用指针优化性能的问题。似乎很多人在这方面都感到困惑。这也可以理解,指针确实是个复杂的话题。 希望这篇文章对你有所帮助。简而言之:不是使用指针就一定代表着性能优化。如果要彻底解释这篇文章涉及的所有细节,那篇幅可能会长到没人愿意看。所以,我精简了一下,试图用中等篇幅也能涵盖想说明的高级概念。阅读时需要说明一点:本文讨论的是微优化,性能优化都是极其细微的。在进行微优化之前,需先进行基准测试,否则很可能看不到明显的效果。代码易读性才是第一要义。什么是指针?指针底层代表的就是内存地址,指针解引用可以访问到内存存储的具体数据值。应用指针之后是如何起到性能优化作用的?函数调用时,变量传递实际上是将变量重新复制了一份,传给函数。多数情况下,指针都要比变量本身占用更小的空间。通常,指针大小和系统的架构体系保持一致。32位系统就是32bit,64位系统,即是64bit大小。像bool、float等标量类型,占用的空间都小于等于指针;而多字段的符合类型,指针占的空间更少。所有,我们的想法就基于复制指针比复制原值更高效。一定程度上,这样想没问题。但是性能问题涉及广泛,除了复制成本之外,还有很多因素要考虑。指针是否会对性能产生负面影响?会。主要出于两方面的考量:解引用虽然耗能很小,但积少成多,不得不虑。通过指针共享的数据,是放在堆上的。堆数据的清理是GC负责的,这也会产生开销。随着堆上数据增多,GC的工作量变大,对项目的性能影响也不容忽视。堆与栈堆和栈是两个让人头疼的概念,但是我们不得不直面它们。我在这尽量用简短的篇幅讨论完。如果没能快速理解也没关系,我曾经也没能很快理解。栈:函数局部空间每当函数被调用,都会分配一块栈空间来存储函数局部变量。函数占用的栈空间大小在编译时已经确定。函数返回时,这块空间就给下一个函数调用使用了,也无需立刻清理。虽然这个分配和使用过程要耗费资源,但相对来讲,消耗很小。堆:共享数据空间如上所述,函数返回后,局部变量会被销毁(译者注:空间被复用或者彻底回收,局部变量不再存在)。如果返回是非指针变量,返回值会被复制给调用者,存在于调用者的栈空间中。但是,如果返回的是指针(译者注:也就是函数局部空间的地址),指针指向的数据就要保存在栈空间之外,这样才能保证函数返回后,数据仍然可以访问。这就是堆的用处。与堆相关的性能问题有这些:堆空间需要从runtime申请,虽然开销很小,也不能不考虑;如果运行时没有足够空间,就需要系统调用了,这是额外的开销;一旦数据占用了堆空间,就要一直占用到没有指针再指向它。这时候,需要GC来清理。GC会找到堆中所有没有被饮用的值,标记为空闲(译者注:请参考Go垃圾回收的三色标记)。垃圾越多,GC耗时越大,系统性能越差。那为什么还要用指针?指针能修改传参,提供了一种共享数据的方式。指针能区分零值,确定你的变量是否被赋值了。总结指针可以节省复制的开销,但同时要考虑解引用和垃圾回收带来的影响。在我看来,性能分析结果显示复制是瓶颈之前,不应该考虑把指针作为优化方案。计算机在复制方面的速度可是极快的。希望这篇文章可以让你认识到指针是可以派上用场的,但也不要因为要用而用。默认还是使用值而不是地址吧。除非语法满足不了你了。

January 21, 2019 · 1 min · jiezi

leetcode讲解--876. Middle of the Linked List

题目Given a non-empty, singly linked list with head node head, return a middle node of linked list.If there are two middle nodes, return the second middle node.Example 1:Input: [1,2,3,4,5]Output: Node 3 from this list (Serialization: [3,4,5])The returned node has value 3. (The judge’s serialization of this node is [3,4,5]).Note that we returned a ListNode object ans, such that:ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL.Example 2:Input: [1,2,3,4,5,6]Output: Node 4 from this list (Serialization: [4,5,6])Since the list has two middle nodes with values 3 and 4, we return the second one.Note:The number of nodes in the given list will be between 1 and 100.题目地址讲解一看到这个题目我就知道用快慢指针。但具体操作的时候有些细节需要注意,要让快慢指针指向第0个位置,而不是head(head算作第一个位置)。Java代码/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */class Solution { public ListNode middleNode(ListNode head) { System.out.println(head.val); ListNode slow = new ListNode(0); ListNode fast = new ListNode(0); slow.next = head; fast.next = head; while(slow!=null && fast!=null){ slow = slow.next; if(fast.next!=null){ fast = fast.next.next; }else{ fast = fast.next; } } return slow; }} ...

December 26, 2018 · 1 min · jiezi

leetcode讲解--922. Sort Array By Parity II

题目Given an array A of non-negative integers, half of the integers in A are odd, and half of the integers are even.Sort the array so that whenever A[i] is odd, i is odd; and whenever A[i] is even, i is even.You may return any answer array that satisfies this condition.Example 1:Input: [4,2,5,7]Output: [4,5,2,7]Explanation: [4,7,2,5], [2,5,4,7], [2,7,4,5] would also have been accepted.Note:2 <= A.length <= 20000A.length % 2 == 00 <= A[i] <= 1000题目地址讲解这道题需要新开一个等大的空间,还需要两个指针来分别记录偶数和奇数的位置。Java代码class Solution { public int[] sortArrayByParityII(int[] A) { int[] result = new int[A.length]; int indexOfEven = 0; int indexOfOdd = 1; for(int i=0;i<A.length;i++){ if(A[i]%2==0){ result[indexOfEven] = A[i]; indexOfEven += 2; }else{ result[indexOfOdd] = A[i]; indexOfOdd += 2; } } return result; }} ...

December 25, 2018 · 1 min · jiezi