LeeCode 283. 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
进阶:你能尽量减少完成的操作次数吗?
代码如下:
写了一个仿照java ArrayList的 可变长数组的数据结构。为了验证,就用它实现本题功能。
IntArrayList.h 头文件:
#ifndef INT_ARRAY_LIST_H
#define INT_ARRAY_LIST_H
// 头文件添加跨语言支持
#ifdef __cplusplus
extern "C" {
#endif
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include <string.h>
typedef int (*Compare)(int, int); // 比较函数,用于排序
typedef struct {
int* data;
int size; // 当前长度,即有多少条数据。
int capacity; // 实际数组长度
}IntArrayList;
IntArrayList* CreateIntArrayList(int initialCapacity);
void FreeIntArrayList(IntArrayList* list);
void ClearIntArrayList(IntArrayList* list);
// 获取指定索引处的数据,因为不修改结构体数据,所以参数list加const修饰
int GetIntArrayList(const IntArrayList* list, int pos);
bool InsertIntArrayList(IntArrayList* list, int val, int pos);
bool AddIntArrayList(IntArrayList* list, int val);
bool SetIntArrayList(IntArrayList* list, int val, int pos);
bool DeleteIntArrayList(IntArrayList* list, int pos);
bool ContainsIntArrayList(const IntArrayList* list, int val);
int SizeIntArrayList(const IntArrayList* list);
void SortIntArrayList(IntArrayList* list, Compare compare);
#ifdef __cplusplus
}
#endif
#endif
相应的,在源文件IntArrayList.c中完成函数定义:
#include "IntArrayList.h"
// 检查数组容量, 容量不够就扩容。 为隐藏内部函数,加static修饰
static bool ensureCapacity(IntArrayList* list) {
if (list->size == list->capacity) { // 实际储存的数据量已经把数组撑满了。
int newCapacity = list->capacity << 1; // 为降低扩容频率,每次容量增加1倍
int* newData = (int*)realloc(list->data, newCapacity * sizeof(int)); //
if (!newData) {
fprintf(stderr, "Memory allocation failed\n");
//exit(EXIT_FAILURE); // 内存分配失败时退出程序
return false;
}
else {
list->data = newData;
list->capacity = newCapacity;
}
}
return true;
}
IntArrayList* CreateIntArrayList(int initialCapacity) {
IntArrayList* list = (IntArrayList*)malloc(sizeof(IntArrayList)); // 堆分配
if (list == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
list->data = (int*)malloc(initialCapacity * sizeof(int));
if (list->data == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE); // 内存分配失败时退出程序
}
list->capacity = initialCapacity;
list->size = 0;
return list;
}
bool AddIntArrayList(IntArrayList* list, int val) {
return InsertIntArrayList(list, val, SizeIntArrayList(list));
}
void FreeIntArrayList(IntArrayList* list) {
free(list->data);
list->data = NULL; // 将指针设置为NULL以避免悬挂指针问题。
list->size = 0;
list->capacity = 0;
free(list);
}
void ClearIntArrayList(IntArrayList* list) {
list->size = 0;
}
int GetIntArrayList(const IntArrayList* list, int pos) {
if (pos >= list->size || pos < 0) {
// 越界了
fprintf(stderr, "out of index\n");
exit(EXIT_FAILURE);
}
return list->data[pos];
}
bool InsertIntArrayList(IntArrayList* list, int val, int pos) {
if (!ensureCapacity(list))
return false;
if (pos < 0 || pos > list->size) {
fprintf(stderr, "InsertIntArrayList, out of index\n");
printf("InsertIntArrayList, out of index , pos: %d, size: %d \n", pos, list->size);
return false;
}
if (pos == list->size) {
// 添加在最后,无需移动其他数据位置
list->data[pos] = val;
list->size++;
return true;
}
// 在pos索引处插入数据,其他数据需要往后移动.
// 先利用memmove函数移动数组中元素
memmove(&list->data[pos + 1], &list->data[pos], (list->size - pos) * sizeof(int));
list->data[pos] = val;
list->size++;
return true;
}
bool SetIntArrayList(IntArrayList* list, int val, int pos) {
if (pos < 0 || pos >= list->size) {
fprintf(stderr, "out of index\n");
return false;
}
list->data[pos] = val;
return true;
}
bool DeleteIntArrayList(IntArrayList* list, int pos) {
if (pos < 0 || pos >= list->size) {
fprintf(stderr, "out of index\n");
return false;
}
if (pos == list->size - 1) {
// 移动的是最后一个
list->size--;
return true;
}
// 将索引pos以及之后所有元素向前移动一个
memmove(&list->data[pos], &list->data[pos + 1], (list->size - pos - 1) * sizeof(int));
list->size--;
return true;
}
bool ContainsIntArrayList(const IntArrayList* list, int val) {
for (int i = 0; i < list->size; i++) {
if (list->data[i] == val)
return true;
}
return false;
}
int SizeIntArrayList(const IntArrayList* list) {
return list->size;
}
void SortIntArrayList(IntArrayList* list, Compare compare) {
if (!list || list->size < 2)
return;
if (!compare) {
printf("没传比较函数!\n");
return;
}
// 先随便想个排序,用冒泡,后面优化为插入法或其他排序方法
int temp;
for (int i = 0; i < list->size - 1; i++) {
// 提前退出冒泡循环的标志位
int swapped = 0;
for (int j = 0; j < list->size - 1 - i; j++) {
if (compare(list->data[j], list->data[j+1]) > 0){
temp = list->data[j];
list->data[j] = list->data[j+1];
list->data[j + 1] = temp;
swapped = 1; // 表示发生了交换
}
}
// 如果没有数据交换,数组已经有序,不需要再继续执行循环
if (!swapped) {
break;
}
}
}
代码:
#include <stdio.h>
#include <stdlib.h>
#include "IntArrayList.h"
int compareFunZeroToLast(int a, int b) { // 目的是排序时把0排到最后
if (a == 0 && b != 0)
return 1;
return 0;
}
void moveZeroes(int* nums, int numsSize) { // LeeCode283. 移动零
IntArrayList* list = CreateIntArrayList(numsSize);
for (int i = 0; i < numsSize; i++) {
if (!AddIntArrayList(list, nums[i])) {
fprintf(stderr, "Failed to add element %d\n", nums[i]);
FreeIntArrayList(list);
return;
}
}
SortIntArrayList(list, compareFunZeroToLast); // 关键是排序
for (int i = 0; i < numsSize; i++) {
nums[i] = GetIntArrayList(list, i); // 保存最终结果
}
}
测试代码:
void testLeeCode283() {
int nums[] = {0, 1, 0, 3, 12};
moveZeroes(nums, sizeof(nums) / sizeof(nums[0]));
printf("结果: \n");
for (int i = 0; i < sizeof(nums) / sizeof(nums[0]); i++) {
printf("%d \n", nums[i]);
}
}
打印:
ok。结果正确。
提交到LeeCode,因为LeeCode不能提交多个文件,所以代码合到一起,这样提交:
int compareFunZeroToLast(int a, int b) { // 目的是排序时把0排到最后
if (a == 0 && b != 0)
return 1;
return 0;
}
typedef int (*Compare)(int, int); // 比较函数,用于排序
typedef struct {
int* data;
int size; // 当前长度,即有多少条数据。
int capacity; // 实际数组长度
}IntArrayList;
// 检查数组容量, 容量不够就扩容。 为隐藏内部函数,加static修饰
static bool ensureCapacity(IntArrayList* list) {
if (list->size == list->capacity) { // 实际储存的数据量已经把数组撑满了。
int newCapacity = list->capacity << 1; // 为降低扩容频率,每次容量增加1倍
int* newData = (int*)realloc(list->data, newCapacity * sizeof(int)); //
if (!newData) {
fprintf(stderr, "Memory allocation failed\n");
//exit(EXIT_FAILURE); // 内存分配失败时退出程序
return false;
}
else {
list->data = newData;
list->capacity = newCapacity;
}
}
return true;
}
IntArrayList* CreateIntArrayList(int initialCapacity) {
IntArrayList* list = (IntArrayList*)malloc(sizeof(IntArrayList)); // 堆分配
if (list == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
list->data = (int*)malloc(initialCapacity * sizeof(int));
if (list->data == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE); // 内存分配失败时退出程序
}
list->capacity = initialCapacity;
list->size = 0;
return list;
}
void FreeIntArrayList(IntArrayList* list) {
free(list->data);
list->data = NULL; // 将指针设置为NULL以避免悬挂指针问题。
list->size = 0;
list->capacity = 0;
free(list);
}
void ClearIntArrayList(IntArrayList* list) {
list->size = 0;
}
int GetIntArrayList(const IntArrayList* list, int pos) {
if (pos >= list->size || pos < 0) {
// 越界了
fprintf(stderr, "out of index\n");
exit(EXIT_FAILURE);
}
return list->data[pos];
}
bool InsertIntArrayList(IntArrayList* list, int val, int pos) {
if (!ensureCapacity(list))
return false;
if (pos < 0 || pos > list->size) {
fprintf(stderr, "InsertIntArrayList, out of index\n");
printf("InsertIntArrayList, out of index , pos: %d, size: %d \n", pos, list->size);
return false;
}
if (pos == list->size) {
// 添加在最后,无需移动其他数据位置
list->data[pos] = val;
list->size++;
return true;
}
// 在pos索引处插入数据,其他数据需要往后移动.
// 先利用memmove函数移动数组中元素
memmove(&list->data[pos + 1], &list->data[pos], (list->size - pos) * sizeof(int));
list->data[pos] = val;
list->size++;
return true;
}
bool SetIntArrayList(IntArrayList* list, int val, int pos) {
if (pos < 0 || pos >= list->size) {
fprintf(stderr, "out of index\n");
return false;
}
list->data[pos] = val;
return true;
}
bool DeleteIntArrayList(IntArrayList* list, int pos) {
if (pos < 0 || pos >= list->size) {
fprintf(stderr, "out of index\n");
return false;
}
if (pos == list->size - 1) {
// 移动的是最后一个
list->size--;
return true;
}
// 将索引pos以及之后所有元素向前移动一个
memmove(&list->data[pos], &list->data[pos + 1], (list->size - pos - 1) * sizeof(int));
list->size--;
return true;
}
bool ContainsIntArrayList(const IntArrayList* list, int val) {
for (int i = 0; i < list->size; i++) {
if (list->data[i] == val)
return true;
}
return false;
}
int SizeIntArrayList(const IntArrayList* list) {
return list->size;
}
bool AddIntArrayList(IntArrayList* list, int val) {
return InsertIntArrayList(list, val, SizeIntArrayList(list));
}
void SortIntArrayList(IntArrayList* list, Compare compare) {
if (!list || list->size < 2)
return;
if (!compare) {
printf("没传比较函数!\n");
return;
}
// 先随便想个排序,用冒泡,后面优化为插入法或其他排序方法
int temp;
for (int i = 0; i < list->size - 1; i++) {
// 提前退出冒泡循环的标志位
int swapped = 0;
for (int j = 0; j < list->size - 1 - i; j++) {
if (compare(list->data[j], list->data[j+1]) > 0){
temp = list->data[j];
list->data[j] = list->data[j+1];
list->data[j + 1] = temp;
swapped = 1; // 表示发生了交换
}
}
// 如果没有数据交换,数组已经有序,不需要再继续执行循环
if (!swapped) {
break;
}
}
}
void moveZeroes(int* nums, int numsSize) {
IntArrayList* list = CreateIntArrayList(numsSize);
for (int i = 0; i < numsSize; i++) {
if (!AddIntArrayList(list, nums[i])) {
fprintf(stderr, "Failed to add element %d\n", nums[i]);
FreeIntArrayList(list);
return;
}
}
SortIntArrayList(list, compareFunZeroToLast);
for (int i = 0; i < numsSize; i++) {
nums[i] = GetIntArrayList(list, i);
}
}
结果:
ok,性能稍差而已。
想起来忘记释放内存了,内存管理是真烦人哪, moveZeroes函数重新贴一下代码:
void moveZeroes(int* nums, int numsSize) { // LeeCode283. 移动零
IntArrayList* list = CreateIntArrayList(numsSize);
for (int i = 0; i < numsSize; i++) {
if (!AddIntArrayList(list, nums[i])) {
fprintf(stderr, "Failed to add element %d\n", nums[i]);
FreeIntArrayList(list);
return;
}
}
SortIntArrayList(list, compareFunZeroToLast);
for (int i = 0; i < numsSize; i++) {
nums[i] = GetIntArrayList(list, i);
}
FreeIntArrayList(list); // 别忘记释放内存!
}
总结,编码中遇过好几个错误。 1、 函数不要返回局部指针变量,局部指针变量,存储在 栈内存 ,若该地址在函数退出后会被回收,导致出现野指针。必须使用 malloc
在堆上分配内存。 2、若c、c++混合编程,头文件需要加跨语言支持。 3、记得释放内存