算法学习笔记(c++不断维护更新)
数组
编程规范性与记笔记重要性
编程规范:
. Googlec++编程规范 ,比较权威,但是具体风格也要以自己团队风格为主;
变量命名方法:主要有
大驼峰:如AppleWeight,第一个单词和后面每个单词首字母均大写
小驼峰:如appleWeight,
下划线命名法;
记笔记:学了一段时间,总是遗忘,手写坚持不下来;还是在csdn或者github上更新维护自己的笔记和想法吧。
工具:markdown(重要性不言而喻)
算法学习路线:数组->链表->哈希表->字符串->双指针法->栈与队列->二叉树->回溯算法->贪心算法->动态规划
Markdown常用语法(用到哪个更新哪个)
力扣牛客(争取遇到的每一个道题目都弄懂并归类)
首先数组学习的意义:非常基础的数据结构,考试时体现代码的掌控能力。想法飞上天,半天憋不出来。
关键知识点:
(1)数组在内存中的存储方式:存放在连续内存空间的相同类型数据集合。连续存储空间决定了移动元素(删除,或者插入)时,其他元素的位置发生变化。
(2)数组的下标是从0开始
(3)二维数组的内存空间是否是连续的。答案是否定的。如a[m][n],其实是m个具有n列的一维数组组成,这些一维数组内部是连续的
2021-03-08更
力扣283:移动零(双指针形式)
class Solution {
public:
void moveZeroes(vector<int>& nums) {
/*与快慢指针(应该是双指针中的一种吧,后面
在双指针中再详细分析)的概念相似,设置两个整型常量 fastIndex,slowIndex;
当下标fastIndex不为零时,填到下
标slowIndex的数组内存中,其余的零再补齐
*/
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++){
if ( nums[fastIndex] != 0){
nums[slowIndex++] = nums[fastIndex];
}
}
for (; slowIndex < nums.size(); slowIndex++){
nums[slowIndex] = 0;
}
}
};
评论区看到另一种双指针的形式代码
class Solution {
public:
//双指针方式:left指针指向有序数组最后一个末尾,right指向右侧待检查是否有零的数组。
//如[1 0 2 3 0 ]一开始指向1。。。。。。
void moveZeroes(vector<int>& nums) {
int n = nums.size(), left = 0, right = 0;
while (right < n) {
if (nums[right]) {
swap(nums[left], nums[right]);
left++;
}
right++;
}
}
};
力扣35:搜索插入位置(二分法)
二分查找的前提是有序数组所以以后只要看到题目里给出的数组是有序数组,都可想一想是否可以使用二分法。
写二分法应该注意的是:自定义left 和right区间的类型。搞清楚啥时候取while中的等于号。这里下面的两段代码是【left right】左闭右闭的区间。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
/*二分法进行查找,最后按顺序被插入的位置(找不到的情况下)
:有left>right,显然位置是left,看到有的题解将temp=((left+right) >> 1 )执行效率会高一些吧 */
int result, left =0, right = nums.size()-1;
while (left <= right){
int temp = (left + right)/2;
if (nums[temp] < target){
left = temp + 1;
}
else if (nums[temp] > target){
right = temp -1;
}
else{
result = temp;
break;
}
}
if(left > right){
//此处使用result = right + 1;亦可,显然如此
result = left;
}
return result;
}
};
评论区:暴力题解不一定时间消耗就非常高,关键看实现的方式,就像二分查找时间消耗不一定就很低。
这个世界没有什么是一定的。下面是Carl哥的实现方式(确实有时会思维僵化,啊!万能的二分查找啊!然后角度不同实现方式不同,暴力并不一定差):
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
for (int i = 0; i < nums.size(); i++) {
// 分别处理如下三种情况
// 目标值在数组所有元素之前
// 目标值等于数组中某一个元素
// 目标值插入数组中的位置
if (nums[i] >= target) {
// 一旦发现大于或者等于target的num[i],那
//么i就是我们要的结果
return i;
}
}
// 目标值在数组所有元素之后的情况
return nums.size(); // 如果target是最大的,或者
//nums为空,则返回nums的长度
}
};
---------------------------------------------------------------------------------------------2021-03-08暂时到这里吧,下面需要去修改论文意见了,有时间再来更新。
----------------------------------------------------------------------------------------------2021-3-13论文意见暂时告一段落,希望安稳度过,审稿人老师高抬贵手;
力扣27移除元素(快慢双指针,这一题和力扣283移除零是一样的)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//思路双指针思维,和移除零一个道理,lowerPoint和fastPointer指针
int lowerPointer = 0, fastPointer = 0,length;
for(; fastPointer < nums.size(); fastPointer++){
if(nums[fastPointer] != val){
nums[lowerPointer++] = nums[fastPointer];
}
}
length = lowerPointer;
for(; lowerPointer < nums.size(); lowerPointer++){
nums[lowerPointer] = val;
}
return length;
}
};
双指针(快慢指针类似题目后期补充位置:力扣15三数目之和,力扣18四数之和,力扣206反转链表,力扣142环形链表)
力扣209长度最小的子数组(滑动窗口法)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
//有些像双指针(快慢指针),slow,fast,不断移动维护窗口的大小
//,自己写的差一点就对了mmp
int slow = 0, fast, minlength = nums.size();
if (nums.size() == 1){
if(nums[0] >= target)
{return 1;}
else
{return 0;}
}
else{
int temp = 0 ;
for(fast = 0; fast < nums.size(); fast++){
temp = temp + nums[fast];
while(temp >= target){
//当前窗口大小
minlength = min((fast - slow + 1),minlength);
temp = temp - nums[slow++];
}
}
if(slow ==0){return 0;}
else{
return minlength;
}
}
}
};
力扣59螺旋矩阵||
还是人家代码写得工整
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 每一圈循环,需要控制每一条边遍历的长度
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < starty + n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < startx + n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
//数组先暂时到这里,下面学习链表方面的知识