卡码网 练习ACM模式 https://kamacoder.com/
1 数组理论基础
推荐链接:https://c.biancheng.net/view/dt3k3gp.html
数组下标都是从0开始的。且数组内存空间的地址是连续的。
正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
使用C++的话,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
数组的元素是不能删的,只能覆盖。在C++中二维数组是连续分布的。
#include <iostream>
using namespace std;
void test_arr(){
int array[2][3] = {
{0,1,2},
{3,4,5}
};
cout<<&array[0][0]<<" "<<&array[0][1]<<" "<<&array[0][2]<<endl;
cout<<&array[1][0]<<" "<<&array[1][1]<<" "<<&array[1][2]<<endl;
}
int main()
{
test_arr();
return 0;
}
0x7fff4913f570 0x7fff4913f574 0x7fff4913f578
0x7fff4913f57c 0x7fff4913f580 0x7fff4913f584
使用数组之前必须先定义数组。定义数组的语法如下:
类型 数组名[数组大小];
int num[10]; //正确
int length = 10;
char str[length]; //错误,length 是变量
const int length2 = 10;
char str[length2]; //正确
char arr[10 + 1]; // 正确
char arr2[length2 + 1]; // 正确
与变量的定义一样,数组也可以连续定义。例如:
int a[10], b[20], c[30];
数组的初始化
数组可以不初始化,但如果你不显式地对数组进行初始化,那么数组的元素值将取决于它们所在的内存位置的初始内容。这可能导致数组中的元素值是未知的或者随机的。
定义数组的同时,为数组中的元素提供初值,这就是所谓的初始化数组。
初始化数组有以下几种方式:
- 直接初始化:int arr[5] = {1, 2, 3, 4, 5};
- 指定部分元素初始化:int arr[5] = {1, 2};,其余元素将被初始化为0。
- 不指定数组大小,根据初始化列表自动确定大小:int arr[] = {1, 2, 3, 4, 5};
注意,初始化数组指的是定义数组的同时为各个元素提供初值。对于先前定义的数组,不能采用初始化的方式指定各个元素的值,例如:
int num[5];
num = {10, 20, 30, 40, 50}; // 错误,不能这样赋值
这样写是错误的,对于先前定义的数组,只能分别为各个元素赋值。
- 同一数组中的所有元素必须是同一类型,尝试存储不同类型的元素会导致编译错误。
- 数组的长度在定义的时候必须确定,不能为变量。
- 定义后的数组,大小就不能改变了,所以在编程时,必须确保数组的大小符合需求。
- 操作数组中的元素,不能超出数组下标的范围。
在Python中,可以通过以下两种方式来定义和使用数组。
-
列表(List)的定义和使用:
创建空列表:my_list = []
添加元素到列表尾部:my_list.append(element)
访问列表元素:value = my_list[index]
修改列表元素:my_list[index] = new_value
删除列表元素:del my_list[index]或者my_list.remove(element)
示例代码如下所示:
my_list=[]
my_list.append('a')
print(my_list)
print(my_list[0])
del my_list[0]
print(my_list)
my_list.append('b')
print(my_list)
my_list.remove(my_list[0])
-
NumPy库提供了多维数组对象ndarray的支持,其定义和使用更加高效和功能丰富。需要先安装NumPy库才能使用该特性。
导入NumPy库:import numpy as np
创建ndarray对象:my_array = np.array([…])
访问ndarray元素:value = my_array[index]
切片操作:sliced_array = my_array[start🔚step]
修改ndarray元素:直接通过索引进行赋值操作
运算符重载:支持常见的数学运算、逐元素计算等操作
示例代码如下所示:
import numpy as np
# 创建ndarray对象
my_array = np.array([1, 2, 3, 4, 5])
# 访问ndarray元素
value = my_array[2]
print(value) # 输出结果为 3
# 切片操作
sliced_array = my_array[1:4:2]
print(sliced_array) # 输出结果为 [2 4]
# 修改ndarray元素
my_array[0] = 6
print(my_array) # 输出结果为 [6 2 3 4 5]
# 运算符重载
result = my_array + 2
print(result) # 输出结果为 [8 4 5 6 7]
17 二分查找
这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0;
int right = nums.size()-1;
int middle;
while(left<=right)
{
middle = (left+right)/2;
if(nums[middle]>target)
{
right=middle-1;
}
else if(nums[middle]<target)
{
left = middle+1;
}
else
{
return middle;
}
}
return -1;
}
};
这里两个注意的点,一个是vector取长度的时候用的是.size(),不是.length()
还有一个是有些自己不知道数组长度的时候,估计官方都给了,不用自己定义。
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left = 0
right = len(nums) - 1
while left <= right:
middle = (left + right) // 2 # 计算中间位置
if nums[middle] > target:
right = middle - 1 # 更新右边界
elif nums[middle] < target:
left = middle + 1 # 更新左边界
else:
return middle # 找到目标值,返回索引
return -1 # 没有找到目标值,返回 -1
18 移除元素
这里要注意删除元素,是不能直接删除的,要覆盖,要把后面的元素移到前面来,然后用vector.size()的话,删除元素,它就会减少,但不是物理意义上的减少。存储空间是不会变的,只是删除的不处理了。
vector里面的erase的删除元素的接口,可是它是一个O(n)的操作,不是O(1)的。
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
i = 0
while i < len(nums):
if nums[i] == val:
nums.pop(i) # 移除当前位置的元素
else:
i += 1 # 指针右移
return len(nums)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int index = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != val) {
nums[index] = nums[i];
index++;
}
}
return index;
}
};
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
nums.erase(std::remove(nums.begin(), nums.end(), val), nums.end());
return nums.size();
}
};
暴力的解法:
```cpp
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i =0;i<size;i++){
if(nums[i]==val){
for(int j = i+1;j<size;j++){
nums[j-1]=nums[j];
}
i--;
size--;
}
}
return size;
}
};
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
size = len(nums)
i = 0
while i < size:
if nums[i] == val:
for j in range(i+1, size):
nums[j-1] = nums[j]
size -= 1
else:
i += 1
return size
小心数组越界,初始化的时候,j=i+1,
双指针法:
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow=0;
int fast = 0;
for (fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){
nums[slow]=nums[fast];
slow++;
}
}
return slow;
}
};
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
slow =0
for fast in range(len(nums)):
if nums[fast]!=val:
nums[slow]=nums[fast]
slow+=1
return slow