0%

链表-反转链表

题目链接

采用双指针方法,并且设置一个临时值保存cur->next的值

1
2
3
4
5
6
7
8
9
10
11
12
13
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* cur = head;
struct ListNode* temp;
struct ListNode* pre = NULL;
while(cur){
temp = cur->next;
cur->next = pre;

pre = cur;
cur = temp;
}
return pre;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ListNode* reverse(struct ListNode* pre, struct ListNode* cur){
if(cur == NULL) return pre;
struct ListNode* tmp = cur->next;
cur->next = pre;
//下面的步骤也和双指针一个逻辑
// pre = cur;
//cur = tmp
return reverse(cur, tmp);
}
struct ListNode* reverseList(struct ListNode* head){
//这里其实和双指针初始化的时候一样
// pre = NULL
// cur = head;
return reverse(NULL, head);
}

链表-设计链表

题目链接

体验一下c和c++的不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(nullptr){}
};
MyLinkedList() {
myhead = new LinkedNode(0);
size = 0;
}

int get(int index) {
if(index >(size-1) || index < 0)return -1;
LinkedNode* cur = myhead->next;
while(index--)cur = cur->next;
return cur->val;
}

void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = myhead->next;
myhead->next = newNode;
size++;
}

void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode *tmp = myhead;
while(tmp->next != nullptr)tmp = tmp->next;
tmp->next = newNode;
size++;
}

void addAtIndex(int index, int val) {
if(index > size) return;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode *tmp = myhead;

while(index--) tmp = tmp->next;
newNode->next = tmp->next;
tmp->next = newNode;
size++;

}

void deleteAtIndex(int index) {
if (index >= size || index < 0) return;
LinkedNode *cur = myhead;
while(index--) cur = cur->next;
LinkedNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
size--;

}
void printLinkedList(){
LinkedNode* cur = myhead;
while(cur->next != nullptr){
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int size;
LinkedNode* myhead;
};

/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
typedef struct {
int val;
struct MyLinkedList* next;
} MyLinkedList;


MyLinkedList* myLinkedListCreate() {
MyLinkedList* head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
head->next = NULL;
return head;
}

int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList* cur = obj->next;
for(int i = 0; cur != NULL; i++){
if(i == index)
return cur->val;
else
cur = cur->next;

}
return -1;
}

void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList* cur = (MyLinkedList*)malloc(sizeof(MyLinkedList));
cur->val = val;
cur->next = obj->next;
obj->next = cur;
}

void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList* cur = obj;
MyLinkedList* tHead = (MyLinkedList*)malloc(sizeof(MyLinkedList));
tHead->val = val;
tHead->next = NULL;
while(cur->next != NULL)
cur = cur->next;
cur->next = tHead;
}

void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if(index == 0){
myLinkedListAddAtHead(obj, val);
return;
}

MyLinkedList* nhead = (MyLinkedList*)malloc(sizeof(MyLinkedList));
nhead->val = val;
MyLinkedList* cur = obj->next;

for(int i = 1; cur != NULL; i++){
if(i == index){
nhead->next = cur->next;
cur->next = nhead;
return;
}
else
cur = cur->next;
}
}

void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if(index == 0){
MyLinkedList *tmp = obj->next;
if (tmp != NULL){
obj->next = tmp->next;
free(tmp);
}
return;
}
MyLinkedList *cur = obj->next;
for (int i = 1 ;cur != NULL && cur->next != NULL; i++){
if (i == index){
MyLinkedList *tmp = cur->next;
if (tmp != NULL) {
cur->next = tmp->next;
free(tmp);
}
return;
}
else{
cur = cur->next;
}
}
}

void myLinkedListFree(MyLinkedList* obj) {
while(obj != NULL){
MyLinkedList *tmp = obj;
obj = obj->next;
free(tmp);
}
}

/**
* Your MyLinkedList struct will be instantiated and called as such:
* MyLinkedList* obj = myLinkedListCreate();
* int param_1 = myLinkedListGet(obj, index);

* myLinkedListAddAtHead(obj, val);

* myLinkedListAddAtTail(obj, val);

* myLinkedListAddAtIndex(obj, index, val);

* myLinkedListDeleteAtIndex(obj, index);

* myLinkedListFree(obj);
*/

链表-移除链表元素

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while(head->val == val && head != NULL){
ListNode* tmp = head; //为后续清理内存暂存元素
head = head->next;
delete tmp;
}
ListNode* cur = head;
while(cur->next != NULL && cur != NULL){
if(cur->next->val == val){
ListNode* tmp = cur->next; //为后续清理内存暂存元素
cur->next = cur->next->next;
delete tmp;
}
else{
cur = cur->next;
}
}

return head;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* myhead = malloc(sizeof(struct ListNode));
myhead->next = head;
struct ListNode* temp = myhead;
while (temp->next != NULL) {
if (temp->next->val == val) {
temp->next = temp->next->next;
} else {
temp = temp->next;
}
}
return myhead->next;
}
1
2
3
4
5
6
7
8
9
10
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
myhead = ListNode(next = head)
cur = myhead
while(cur.next != None):
if(cur.next.val == val):
cur.next = cur.next.next
else:
cur = cur.next
return myhead.next

python可真是简单了,c/c++需要自己释放删掉的元素。

数组-螺旋矩阵

题目链接

这道题刚刚看到的时候很纳闷,一脸的什么玩意,仔细理清楚思路和规律以及限制就知道了

​ 生成一个 n×n 空矩阵 matrix,随后模拟整个向内环绕的填入过程:定义当前左右上下边界 left,right,above,below,初始值 num = 1,迭代终止值 tar = n * n;当 num <= tar 时,始终按照 从左到右 从上到下 从右到左 从下到上 填入顺序循环,每次填入后:执行 num += 1:得到下一个需要填入的数字;
​ 更新边界:例如从左到右填完后,上边界 t += 1,相当于上边界向内缩 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector< vector<int>> matrix(n, vector<int>(n, 0));
int left = 0;
int right = n-1;
int above = 0;
int below = n-1;
int tar = n*n;
int num = 1;
while(num <= tar){
for(int i = left; i <= right; i++)
matrix[above][i] = num++;
above++;
for(int i = above; i <= below; i++)
matrix[i][right] = num++;
right--;
for(int i = right; i >= left; i--)
matrix[below][i] = num++;
below--;
for(int i = below; i >= above; i--)
matrix[i][left] = num++;
left++;

}
return matrix;
}
};

外层分析

1.先查壳,看到是C#写的程序,IDA分析也分析不出来个啥,就是个GUI,主要功能还是在那个dll,dll导出函数如下,主要看看checkcode

image-20220309111717495

分析之前用dnspy看看exe主要逻辑,当输入的code和id满足条件的时候就会调用dll里的checkcode进行逻辑判断,运行调试时,程序窗口不会出现,应该是有反调试。

image-20220309113311485

image-20220309112558876

输入的ID和code进行异或之后等于给定的array即可,异或次数为MAX(len(ID),len(Code)),长度不够的与自身长度进行取模

id固定为9个字节,code应该会比ID长(因为AES需要十六字节)

1
2
3
4
5
6
7
8
9
10
11
array = [7,90,115,1,117,99,114,97,24]
id = array
# code应该是乱写的,16位
code = list(b'))bba!!!(@@###qq')
for i in range(len(code)):
id[i%9] = code[i]^id[i%9]
idstr = ''
for i in range(9):
idstr+=chr(id[i])
print(idstr)

动态调试

2.用IDA打开dll,点进checkcode,然后用FindCrypt插件查看到用了AES加密

image-20220309161731207

函数最后将加密之后的输入和一个32字节的数据进行比较并返回结果。

image-20220309162617066

x64dbg打开程序,用API断点到IsDebuggerPresent,我们需要修改PEB结构中的BeingDebugged改为0,下面是函数的实现

所以我们输入以下命令,然后将值修改为0.(CTRL+E)

image-20220309172520848

image-20220309171841449

如上就可以跟踪到dll中的某个反调试函数。

复制当前指令的首字节地址,用ida打开dll并跳转到复制的地址。按照x64dbg的代码将函数名字修改如下

image-20220310112639564

现在需要点击X,查找一下哪里引用了该函数

image-20220310112849790

image-20220310112932194

发现在sub_18000013F0中用了该函数,并且采用的是创建线程的方式,将createThread第五个参数patch为0x00000004(CREATE_SUSPENDED)

因为dll是64位的,所以查了一下调用约定,前四位参数按顺序存放在rcx, rdx, r8, r9,后面的部分存放在堆栈,下面图中,rsp+20h就是第五个参数,rsp+28h就是第六个参数(调试发现rax为0000000000000000,eax是rax后面8位0)

发现修改后代码占据大小变了。所以直接全部nop然后保存

image-20220310170155735

image-20220310195651299

动态调试修改后的dll,就可以绕过反调试部分,其实后面发现不需要绕过该部分反调试,因为iv是全局变量,dll一家在就有数据了,所以可能通过实际加载的Imagebase计算获得key和iv向量的地址,提取数据即可。右键直接在转储中跟随,就可以得到key和iv的值

image-20220311124512305

CheckCode分析

通过这部分的分析我们得获得正确的code,图中圈起来的就是我们前面所提取的iv和key.

image-20220311124651488

函数在后面会和另一个字符串比较

image-20220311124826719

image-20220311124925694

参考了网上的博客写了如下解决方法,计算得code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# This is a sample Python script.

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.


from Crypto.Cipher import AES
sbox = [[0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76],
[0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0],
[0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15],
[0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75],
[0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84],
[0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF],
[0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8],
[0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2],
[0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73],
[0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB],
[0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79],
[0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08],
[0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A],
[0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E],
[0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF],
[0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]]
def SubBytes(state):
return [sbox[i][j] for i,j in [(t>>4,t&0xf) for t in state]]
def Bytesxor(data1,data2):
data1=list(data1)
data2=list(data2)
res=[]
for i in range(len(data1)):
res.append(data1[i]^data2[i])
return bytearray(res)
key=bytearray(b'\x29\x23\xBE\x84\xE1\x6C\xD6\xAE\x52\x90\x49\xF1\xF1\xBB\xE9\xEB')
iv=bytearray(b'\xB3\xA6\xDB\x3C\x87\x0C\x3E\x99\x24\x5E\x0D\x1C\x06\xB7\x47\xDE')
keyarray=[]
keyarray.append(key)
ivarray=[]
ivarray.append(iv)
data=b'\xF6\x1C\xE3\xD7\xF9\xFB\x0B\x1A\x8B\xA2\x1D\xD8\x97\x94\x05\xC4\x6D\x97\xE7\x62\xB6\x7C\xEF\x9A\x88\x1B\xA4\x4D\xFD\xB0\xE4\x6E'
for i in range(31):
iv=bytearray(SubBytes(iv))
key=Bytesxor(key, iv)
key=bytearray(SubBytes(key))
keyarray.append(key)
iv=Bytesxor(key, iv)
ivarray.append(iv)
keyarray.reverse()
ivarray.reverse()
print(len(keyarray))
for i in range(32):
key=keyarray[i]
iv=ivarray[i]
aes=AES.new(bytes(key), AES.MODE_CBC,bytes(iv))
data=aes.decrypt(data)
print(data)
'''
iv=subbytes(iv)
key=key^iv
key=subbytes(key)
iv^=key
'''
'''
key
29 23 BE 84 E1 6C D6 AE 52 90 49 F1 F1 BB E9 EB
1B C5 C5 A8 42 4F 43 09 43 E8 0B 3C 0B C9 3B 42
26 27 03 37 AE 17 98 5E 1D 76 5D 86 52 D4 15 5D
20 56 F3 DF E4 A7 7E E5 70 68 69 D5 70 F7 F9 C9
'''

image-20220311131051108

然后有个外层逻辑,也就是前面提到的,dnspy已经反编得很好认了,python代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from z3 import *
flag2 = bytearray(b'Meaningless_!$!%*@^%#%_Code')
id = [BitVec('a%d' % i, 8) for i in range(9)]
fuckId = [i for i in id]
target =[7, 90, 115, 1, 117, 99, 114, 97, 24]
tt = bytearray(target)
for j in range(len(flag2)):
id[j % len(target)] ^= flag2[j % len(flag2)]
s = Solver()
for i in range(9):
s.add(id[i] == target[i])
s.check()
res = s.model()
id_ = ''
for i in range(9):
id_ += chr(res[fuckId[i]].as_long())
print(id_)

得到得ID是ginkgo_CX

所以得到flag{ginkgo_CX@Meaningless_!$!%*@^%#%_Cod}

参考链接:这篇很详细还有其他

tips题外话

python代码中肯呢会提示没有模块名叫Crypto,如果要安装的话肯能会出现这样的问题,按照第二张图片的命令安装就好了

1
pip install pycryptodome -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

image-20220311130907869

image-20220311130933815

数组-长度最小的子数组

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = nums.size()+1;
int sum = 0;
int length = 0;
for(int i = 0; i < nums.size(); i++){
sum = 0;
for(int j = i; j < nums.size(); j++){
sum += nums[j];
if(sum >= target){
length = j-i+1;
result = result < length ? result : length;
break;
}
}
}
return result == nums.size()+1 ? 0 : result;
}
};

上面又是暴力的方法,复杂度O(n^2),使用双指针试试,设定两个指针left 和right分别为子数组的开始和结束位置,最开始都为0,每一轮迭代都将nums[right]加到sum中,如果sum>=target,那么就更新子数组长度以及将nums[left]移出sum,并将nums[left]右移,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = nums.size()+1;
int sum = 0;
int length = 0;
int left = 0;
for(int right = left; right < nums.size(); right++){
sum += nums[right];
while(sum >= target){
length = right-left+1;
result = result < length ? result : length;
sum -= nums[left++];
}
}

return result == nums.size()+1 ? 0 : result;
}
};

今天看到一个很好的非常基础详细的贴子关于栈与栈帧,文中对于栈帧做了一个小总结

​ 栈帧就是利用EBP(栈帧指针,注意不是ESP)寄存器访问栈内局部变量、参数、函数返回地址等的手段。ESP寄存器承担着栈顶指针的作用,而EBP寄存器则负责行驶栈帧指针的职能。在程序运行中,ESP寄存器的值随时会变化,访问栈中函数的局部变量、参数时,若以ESP的值为基准编写程序会十分困难,并且也很难使CPU引用到准确的地址。所以,调用某函数时,先要把用作基准点(函数起始地址)的ESP值保存到EBP,并维持在函数内部。这样,无论ESP的值如何变化,以EBP的值为基准能够安全访问到相关函数的局部变量、参数、返回地址。

image-20220307163010502

栈帧

​ 函数调用经常是嵌套的,在同一时刻,堆栈中会有多个函数的信息。每个未完成运行的函数占用一个独立的连续区域,称作栈帧 (Stack Frame)。栈帧是堆栈的逻辑片段,当调用函数时逻辑栈帧被压入堆栈, 当函数返回时逻辑栈帧被从堆栈中弹出。栈帧存放着函数参数,局部变量及恢复前一栈帧所需要的数据等。

​ 栈帧的边界由栈帧基地址指针 EBP 和堆栈指针 ESP 界定 (指针存放在相应寄存器中)。EBP 指向当前栈帧底部 (高地址),在当前栈帧内位置固定;ESP 指向当前栈帧顶部 (低地址),当程序执行时 ESP 会随着数据的入栈和出栈而移动。因此函数中对大部分数据的访问都基于 EBP 进行。

image-20220405163142087

数组-有序数组的平方

题目链接

1
2
3
4
5
6
7
8
9
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++)
nums[i] *= nums[i];
sort(nums.begin(),nums.end());
return nums;
}
};

如果用c++的话,如上的方法是最简单的,但是呢时间复杂度不符合进阶要求。

采用双指针方法,因为数组是有序的,头和尾的平方才有可能是最大值,不可能在中间,所以可以设定一个左指针和右指针,再定义一个数组来存放平方值,数组和原数组大小一样,设定一个k指针指向数组最终位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
int k = nums.size() - 1;
vector<int> result(nums.size(), 0);
while(left <= right){
if(nums[left] * nums[left] < nums[right] * nums[right]){
result[k--] = nums[right] * nums[right];
right--;
}
else{
result[k--] = nums[left] * nums[left];
left++;
}
}
return result;
}
};

1.先查壳,看到是Enigma Virtual Box,是一个虚拟文件打包系统,可以将程序和配套文件打包成一个可执行文件

image-20220307105218538

image-20220307111745958

  1. 脱壳后拖进IDA,在WinMain函数里看不出来个啥,用OD试试。

image-20220307112956567

先查找所有字符串,看到这里大概是flag,点击去

image-20220307113542366

然后到IDA中找到相应位置,f5反汇编得到如下

image-20220307145252803

image-20220307144339188

image-20220307144352256

这里是函数sub_4012F0(),可以看到很明显用的是Base58加密,加密后与“56fkoP8KhwCf3v7CEz”字符串进行对比,所以直接找一个网络工具解码得到

flag{12t4tww3r5e77}

数组-移除元素

题目链接

1
2
3
4
5
6
7
8
9
10
11
12
13
int removeElement(int* nums, int numsSize, int val){
for(int i = 0; i < numsSize; i++){
if(nums[i] == val){
for(int j = i + 1; j < numsSize;j++){
nums[j-1] = nums[j];
}
i--;
numsSize--;
}

}
return numsSize;
}

这样的党法有一点笨拙,复杂度太高(O(n^2)),看了一下题解的双指针方法:通过一个右指针和左指针在一个For循环下完成两个for循环的工作

右指针 right 指向当前将要处理的元素,左指针left 指向下一个将要赋值的位置。

  • 如果右指针指向的元素不等于val,它一定是输出数组的一个元素,我们就将右指针指向的元素复制到左指针位置,然后将左右指针同时右移;

  • 如果右指针指向的元素等于 val,它不能在输出数组里,此时左指针不动,右指针右移一位。

1
2
3
4
5
6
7
8
9
10
int removeElement(int* nums, int numsSize, int val) {
int left = 0;
for (int right = 0; right < numsSize; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}