定义:就是一个个举例出来,然后看看符不符合条件。
举例:一个数组中的数互不相同,求其中和为0的数对的个数。
for (int i = 0; i < n; ++i)
for (int j = 0; j < i; ++j)
if (a[i] + a[j] == 0) ++ans;
题目 难度:中等
class Solution {
public:
bool isPrime(int x) //判断x是否是质数
{
for (int i = 2; i * i <= x; ++i)
{
if (x % i == 0)
{
return false;//不是质数
}
}
return true;//是质数
}
int countPrimes(int n) {
int ans = 0;
for (int i = 2; i < n; ++i) {
ans += isPrime(i);
}
return ans;
}
};
class Solution
{
public:
int countPrimes(int n)
{
vector<int> isPrime(n, 1);//一开始全标记为1
int ans = 0;
for (int i=2; i<n; ++i)
{
if (isPrime[i])
{
ans += 1;//2,3必是质数
for (int j=2*i; j<n; j+=i)
{
isPrime[j]=0;
//i是质数,i的倍数(j)肯定不是质数,赋值为0
}
}
}
return ans;
}
};
题目 难度:简单
#include
using namespace std;
//判断n是否是素数
int check(int n)
{
for(int i=2;i<n;i++)
{
if(n%i==0) return 0;//不是素数
}
return 1;//是质数
}
int main()
{
int len=0;
int gc;//公差
int num;//序列的第一个数
int ans=0;
for(num=2;num<=1000;num++)
{
if(check(num))//检查第一个数字是不是素数
{
for(gc=1;gc<=1000;gc++)//从1开始枚举公差
{
for(int j=1;j<1000;j++)
{
if(check(num+j*gc)) ans++;//代表是素数
else
{
ans=0;
break;//推出当前for循环
}
if(ans==9) //从0开始计数,ans=0时就已经有一个
{
cout<<gc;
return 0;
}
}
}
}
}
return 0;
}
题目 难度:简单
class Solution {
public:
int countTriples(int n)
{
int ans=0;
for(int a=1;a<=n;a++)
{
for(int b=a+1;b<+n;b++)
{
for(int c=b+1;c<=n;c++)
{
if(a*a+b*b==c*c) ans+=1;
}
}
}
return ans*2;
}
};
题目 难度:简单
class Solution {
public:
int arithmeticTriplets(vector<int>& nums, int diff)
{
int size=nums.size();
int num=0;//算术三元组的数目
for(int i=0;i<size;i++)
{
for(int j=i+1;j>i&&j<size;j++)
{
if(nums[j]-nums[i]==diff)
{
for(int k=j+1;k>j&&k<size;k++)
{
if(nums[k]-nums[j]==diff)
{
num=num+1;
break;//退出当前for(k)循环
}
else continue;
}
}
else continue;
}
}
return num;
}
};
class Solution {
public:
int arithmeticTriplets(vector<int>& nums, int diff) {
int n = nums.size();
unordered_set<int> st;
for (int x : nums) st.insert(x);//将nums数组里的值插入到哈希表st中
int ans = 0;
for (int i = 0; i < n; i++)
{
if (st.count(nums[i] +diff) > 0 && st.count(nums[i] + 2*diff) > 0)
{
ans++;
}
}
return ans;
}
};
题目 难度:简单
class Solution {
public:
int commonFactors(int a, int b)
{
int min_data=min(a,b);//找a,b之间的最小值
int num;
for(int i=1;i<=min_data;i++)//公因数一定小于a和b的最小值
{
if(a%i==0&&b%i==0)//公因子的定义
{
++num;
}
}
return num;
}
};
题目 难度:中等
class Solution {
public:
long long waysToBuyPensPencils(int total, int cost1, int cost2)
{
int total1=total/cost1;//最多买几支钢笔
int total2=total/cost2;//最多买几支铅笔
long long num=0;
for(int i=0;i<=total1;i++)
{
if(total-i*cost1>=0)//买完钢笔后还能买几只铅笔
{
int new_total=total-i*cost1;
num+=new_total/cost2;
num=num+1;
}
}
return num;
}
};
题目 难度:中等
class Solution {
public:
int minimumNumbers(int num, int k)
{
if(num==0) return 0;//当 num=0时,唯一的方法是选择一个空集合,答案为0
for(int i=1;i<=10;i++)//num>0时,我们可以发现最多不会选择超过10个数。
//这是因为如果这些数的个位数字为 k,并且我们选择了至少 11个数,由于11*k(10*k+k)的个位数字也为k,那么我们可以把任意的11个数合并成1个,使得选择的数仍然满足要求,并且集合更小。
{
if(i*k<=num&&(num-i*k)%10==0) return i;
//i*k<=num:由于每个数最小为 k,那么这 i 个数的和至少为 i⋅k。如果i⋅k>num,那么无法满足要求。
//这 i 个数的和的个位数字已经确定,即为 i*k mod 10。
//我们需要保证其与 num 的个位数字相同,这样 num−i⋅k 就是 10 的倍数,我们把多出的部分加在任意一个数字上,都不会改变它的个位数字。
}
return -1;
}
};
定义:模拟就是用计算机来模拟题目中要求的操作。
题目 难度:简单
#include
using namespace std;
#include
int main()
{
int n,u,d;
cin>>n>>u>>d;
int x=0;//爬的高度
int num=0;//爬的分钟数
while (true)
{ // 用死循环来枚举
x += u;
num++;//爬一分钟
if (x>= n) break; // 满足条件则退出死循环
num++;//休息一分钟
x -= d;
}
cout<<num;
return 0;
}
题目 难度:简单
class Solution {
public:
vector<int> buildArray(vector<int>& nums)
{
int size=nums.size();
vector<int> ans(size,0);
for(int i=0;i<size;i++)
{
ans[i]=nums[nums[i]];
}
return ans;
}
};
题目 难度:简单
#include
using namespace std;
int main()
{
// 请在此输入您的代码
for(int i=1;i<1000;i++)
{
for(int j=i;j<1000;j++)
{
for(int k=j;k<1000;k++)
{
if(i*i+j*j+k*k==1000&&i!=6&&j!=8&&k!=30)
{
cout<<min(min(i,j),k);
return 0;
}
}
}
}
return 0;
}
题目 难度:简单
#include
using namespace std;
#include
#include
int main()
{
// 请在此输入您的代码
int n;
cin >> n;
vector<int> arr(n);
int diff;
int ans = n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
sort(arr.begin(), arr.end());
//cout << "从小到大排序后:";
//for (int i = 0; i < n; i++)
//{
//cout << arr[i] <<" ";
//}
//cout << endl;
vector<int> diff_vector(n-1);
int min_value;
for (int j = 0; j < n -1; j++)
{
diff_vector[j] = arr[j + 1] - arr[j];
}
min_value = *min_element(diff_vector.begin(), diff_vector.end());
//cout << min_value << endl;;
for (int k = 0; k < n - 1; k++)
{
if (arr[k + 1] - arr[k] != min_value)
{
diff = arr[k + 1] - arr[k];
ans += (diff / min_value)-1;
}
}
if (arr[1] == arr[0]) cout << n;
else cout << ans;
return 0;
}
题目 难度:简单
#include
#include
using namespace std;
int main()
{
// 请在此输入您的代码
int ans=0;//填数方案
int num[]={0,1,2,3,4,5,6,7,8,9};
do{
if(abs(num[0]-num[1])!=1&&abs(num[0]-num[3])!=1
&&abs(num[0]-num[4])!=1&&abs(num[0]-num[5])!=1
&&abs(num[1]-num[4])!=1&&abs(num[1]-num[2])!=1
&&abs(num[1]-num[5])!=1&&abs(num[1]-num[6])!=1
&&abs(num[2]-num[5])!=1&&abs(num[2]-num[6])!=1
&&abs(num[3]-num[4])!=1&&abs(num[3]-num[7])!=1&&abs(num[3]-num[8])!=1
&&abs(num[4]-num[5])!=1&&abs(num[4]-num[7])!=1
&&abs(num[4]-num[8])!=1&&abs(num[4]-num[9])!=1
&&abs(num[5]-num[8])!=1&&abs(num[5]-num[9])!=1&&abs(num[5]-num[6])!=1
&&abs(num[6]-num[9])!=1&&abs(num[7]-num[8])!=1&&abs(num[8]-num[9])!=1){ans++;}
}while(next_permutation(num,num+10));
cout<<ans<<endl;
return 0;
}
next_permutation 全排列函数
需要引用的头文件:
#include
函数原型:
bool std::next_permutation<int *>(int *_First, int *_Last)
(1)基本格式:
int a[];
do{
//循环体
}while(next_permutation(a,a+n));//表达式
//全排列生成好了next_permutation函数返回0,会跳出while循环。
(2)举例:
#include
#include
#include
#using namespace std;
int main()
{
string s = "aba";
sort(s.begin(), s.end());//排序aab
do {
cout << s << '\n';//先执行一次这行,再去执行while的表达式
} while(next_permutation(s.begin(), s.end()));
//do…while 是先执行一次循环体,然后再判别表达式。
//当表达式为“真”时,返回重新执行循环体,如此反复,直到表达式为“假”为止,此时循环结束。
}
//输出:aab
//aba
//baa
#include
#include
using namespace std;
int main()
{
int a[4] = { 0 }, n;
cin >> n;
for (int i = 0; i <n; i++)
{
cin >> a[i];
}
do{
for (int i = 0; i <n; ++i)
{
cout << a[i] << " ";
}
cout << endl;
} while (next_permutation(a, a + n));
return 0;
}
//输入:4
//2 5 1 3
//输出:
//2 5 1 3
//2 5 3 1
//3 1 2 5
//3 1 5 2
//3 2 1 5
//3 2 5 1
//3 5 1 2
//3 5 2 1
//5 1 2 3
//5 1 3 2
//5 2 1 3
//5 2 3 1
//5 3 1 2
//5 3 2 1
递归代码最重要的两个特征:结束条件和自我调用。自我调用是在解决子问题,而结束条件定义了最简子问题的答案。
递归的缺点:在程序执行中,递归是利用堆栈来实现的。每当进入一个函数调用,栈就会增加一层栈帧,每次函数返回,栈就会减少一层栈帧。而栈不是无限大的,当递归层数过多时,就会造成栈溢出的后果。
如何优化递归:深度优先搜索(DFS)/记忆化搜索(动态规划的一种)
题目 难度:中等
使用递归会出现的问题:终止条件需要使用 if ,因此本方法不可取。
思考:除了 if还有什么方法?答:逻辑运算符
a&&b
如果a是false,那么就不会继续执行b
a||b
如果a是true,就不会继续执行b
class Solution {
public:
int num=0;
int sum=0;//总和
int sumNums(int n)
{
//递归
num=n;
sum+=num;
--num;
num>=1&&sumNums(num);//也可换成num==0||sumNums(num);
return sum;
}
};
题目 难度:简单
class Solution {
public:
bool isPowerOfTwo(int n)
{
if(n==1) return true;//终止条件
if(n<=0) return false;
bool res=false;
if(n%2==0)
{
n=n/2;
res=isPowerOfTwo(n);
}
return res;
}
};
题目 难度:简单
class Solution {
public:
int fib(int n)
{
//截至条件
if(n==0) return 0;
if(n==1) return 1;
//递推关系
return fib(n-1)+fib(n-2);
}
};
术语:
(1)稳定排序:如果 a 原本在 b 的前面,且 a=b,排序之后 a 仍然在 b 的前面,则为稳定排序。
(2)非稳定排序:如果 a 原本在 b 的前面,且 a=b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
(3)原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。
(4)非原地排序:需要利用额外的数组来辅助排序。
//从小到大排序
template<typename T>
void selection_sort(vector<T>& arr)
{
for (int i = 0; i < arr.size()-1; i++)
{
int min = i;
for (int j = i + 1; j < arr.size(); j++)
{
if (arr[j] < arr[min])
{
min = j;//记录最小值
}
}
swap(arr[i], arr[min]);//交换i与min对应的值
}
}
题目 难度:简单
class Solution {
public:
vector<string> sortPeople(vector<string>& names, vector<int>& heights)
{
for (int i = 0; i < heights.size()-1; i++)
{
int max = i;
for (int j = i + 1; j < heights.size(); j++)
{
if (heights[j] > heights[max])
{
max = j;//记录最大值
}
}
swap(heights[i],heights[max]);//交换i与max对应的值
swap(names[i], names[max]);
}
return names;
}
};
//从小到大排序
void insertion_sort(int a[], int n)
{
for(int i= 1; i<n; i++)
{
int key = a[i];//从第二个元素开始抽取数据key
int j= i-1;//i-1是因为要从key左边第一个元素开始比较
while(j>=0&&a[j]>key)//key与下标位j,j-1....的元素一个个比较过去。采用顺序查找方式找到插入的位置,在查找的同时,将数组中的元素进行后移操作,给插入元素腾出空间
{
a[j+1] = a[j];
j--;
}
a[j+1] = key;//插入到正确位置,这里j+1是因为上面的j--
}
}
题目 难度:简单
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums)
{
//耗时太长
//先平方整个数组
for(int i=0;i<nums.size();i++)
{
nums[i]=nums[i]*nums[i];
}
//插入排序
for(int i=1;i<nums.size();i++)
{
int key=nums[i];
int j=i-1;
while(j>=0&&key<nums[j])//当key>=nums[]时,把key插到num[i]的右边
{
nums[j+1]=nums[j];
j--;
}
nums[j+1]=key;//这里j+1是因为上面j--
}
return nums;
}
};
题目难度:中等
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* insertionSortList(ListNode* head)
{
if (head == nullptr)
//首先判断给定的链表是否为空,若为空,则不需要进行排序,直接返回。
{
return head;
}
ListNode* dummyHead = new ListNode(0);
//创建哑节点 dummyHead,令 dummyHead.next = head。
//引入哑节点是为了便于在 head 节点之前插入节点。
dummyHead->next = head;
//维护lastSorted为链表的已排序部分的最后一个节点,初始时 lastSorted = head。
ListNode* lastSorted = head;//已经排好序的最后一个节点
ListNode* curr = head->next;//维护 curr 为待插入的元素,初始时 curr = head.next
//举例head = [-1,5,3,4,0]
while(curr!=nullptr)
{
if(curr->val>=lastSorted->val)//5>-1
//说明curr应该位于 lastSorted 之后,将 lastSorted 后移一位,curr 变成新的 lastSorted。
{
lastSorted=lastSorted->next;
}
else//3<5
//否则,从链表的头节点开始往后遍历链表中的节点,寻找插入 curr的位置。令 prev为插入 curr的位置的前一个节点,进行如下操作,完成对curr的插入
{
ListNode *prev=dummyHead;
while (prev->next->val <= curr->val)
//从头开始遍历,直到遇到一个prev->next->val > curr->val,那么curr就要插到prev与prev->next之间:prev->curr->prev.next
{
prev = prev->next;
}
lastSorted->next = curr->next;//这里不懂?
curr->next=prev->next;
prev->next=curr;
}
//令curr=lastSorted.next,curr就成为下一个待插入的元素
curr=lastSorted->next;//每排完一个数字,就把curr往后移动
}
return dummyHead->next;
}
};
void shell_sort(vector<int>& arr,int n)
{
int key;
int j;
//初始增量inc为n/2
for(int inc=n/2;inc>0;inc/=2)//假设inc=12/2,6/2=3,3/2=1;
{
//每一趟采用插入排序
for(int i=inc;i<n;i++)//搞清楚这里为什么是i++,因为是从inc开始一直到n-1,让inc和0,inc+1和1,inc+2和2……n-1与n-1-inc两两相比较
{
key=arr[i];
for(j=i;j>=inc&&arr[j-inc]>key;j-=inc)
{
arr[j]=arr[j-inc];
}
arr[j]=key;
}
}
}
题目 难度:中等
class Solution {
public:
vector<int> sortArray(vector<int>& nums)
{
int n=nums.size();
int key;
int j;
//初始增量inc为n/2
for(int inc=n/2;inc>0;inc/=2)//假设inc=12/2,6/2=3,3/2=1;
{
//每一趟采用插入排序
for(int i=inc;i<n;i++)//搞清楚这里为什么是i++,因为是从inc开始一直到n-1,让inc和0,inc+1和1,inc+2和2……n-1与n-1-inc两两相比较
{
key=nums[i];
for(j=i;j>=inc&&nums[j-inc]>key;j-=inc)
{
nums[j]=nums[j-inc];
}
nums[j]=key;
}
}
return nums;
}
};
#include
using namespace std;
template<typename T> //整数或浮点数皆可使用,若要使用类(class)或结构体(struct)时必须重载大于(>)运算符
//从小到大排序
void bubble_sort(T arr[], int len) //len是arr的长度
{
int i, j;
for (i = 0; i < len - 1; i++)//轮数
{
for (j = 0; j < len - 1 - i; j++)//每一轮要交换几次
{
if (arr[j] > arr[j + 1])
{
swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
int arr[] = { 61, 17, 29, 22, 34, 60, 72, 21, 50, 1, 62 };
int len = (int) sizeof(arr) / sizeof(*arr);
bubble_sort(arr, len);
for (int i = 0; i < len; i++)
cout << arr[i] << ' ';
cout << endl;
float arrf[] = { 17.5, 19.1, 0.6, 1.9, 10.5, 12.4, 3.8, 19.7, 1.5, 25.4, 28.6, 4.4, 23.8, 5.4 };
len = (float) sizeof(arrf) / sizeof(*arrf);
bubble_sort(arrf, len);
for (int i = 0; i < len; i++)
cout << arrf[i] << ' '<<endl;
return 0;
}
//a是待排序数组,b是排序后的数组,cnt是额外数组
//从小到大排序
void counting_sort()
{
if(a.size()<2) return;
//寻找最大元素
//int max=a[0];
//for(int i=0;i
//{
//if(a[i]>max) max=a[i];
//}
int max = *max_element(heights.begin(), heights.end());
// void *memset(void *str, int c, size_t n) :复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
//分配一个长度为(max-min+1)的计数数组来存储计数
// memset(cnt, 0, sizeof(int)*(max-min+1));//cnt变成全0数组,清零
vector<int> count(max+1,0);//下标为0,1,2,……,max
//计数
for (auto x:a) ++cnt[x];//统计数组中每个值为x的元素出现的次数,存入数组cnt的第x项
for (int i = 1; i <= w; ++i) cnt[i] += cnt[i - 1];//统计出现次数,w是cnt的长度
for (int i=0;i<len;i++
)
{
b[cnt[a[i]]-1] = a[i];
--cnt[a[i]];
}//反向填充目标数组,n是数组a的长度
}
题目 难度:简单
注意到本题中学生的高度小于等于100,因此可以使用计数排序。
class Solution {
public:
int heightChecker(vector<int>& heights)
{
int len=heights.size();
int num=0;
//寻找最大元素
// int max=a[0];
// for(int i=0;i
// {
// if(a[i]>max) max=a[i];
// }
int max = *max_element(a.begin(), a.end());
//计数数组初识化为0
vector<int> count(max+1,0);//下标为0,1,2,……,max
//计数
for(int i=0;i<len;i++)
{
count[heights[i]]++;
}
//统计计数的累计值
for(int i=1;i<max+1;i++)
{
count[i]+=count[i-1];
}
//创建输出数组expected
vector<int> expected(len);
for(int i=0;i<len;i++)
{
expected[count[heights[i]]-1]=heights[i];//count[heights[i]]-1是元素正确的位置
count[heights[i]]--;
}
//找不同的下标数量
for(int i=0;i<len;i++)
{
if(heights[i]!=expected[i]) num++;
}
return num;
}
};
方法1:双指针法
int partition(int r[], int low, int high) //划分函数
{
int i = low, j = high, pivot = r[low]; //基准元素
while (i < j)
{
while (i<j && r[j]>pivot) //从右向左开始找一个 小于等于 pivot的数值
{
j--;
}
if (i < j)//退出while循环,这里其实已经找到了一个小于等于pivot的数
{
swap(r[i], r[j]);
i++; //r[i]和r[j]交换后 i 向右移动一位
}
while (i < j && r[i] <= pivot) //从左向右开始找一个 大于 pivot的数值
{
i++;
}
if (i < j)
{
swap(r[i], r[j]); //r[i]和r[j]交换后 i 向左移动一位
j--;
}
}
return i; //返回最终划分完成后基准元素所在的位置
}
void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high)
{
int pivot = Paritition(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
}
方法2:挖坑填数法
int partition(int r[], int l, int r)
{
if (l< r)
{
int i = l, j = r, x = r[l];//相当于r[l]被挖掉存在x里面了
while (i < j)
{
while(i < j && r[j]>= x) // 从后向前找第一个小于x的数,找到就r[i]=r[j]
{
j--;
}
if(i < j)
{
r[i] = r[j];
i++;
}
while(i < j && r[i]< x) // 从前向后找第一个大于等于x的数
i++;
if(i < j)
{
r[j] = r[i];
j--;
}
}
return i;
}
}
void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high)
{
int pivot = Paritition(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
}
题目
#include
using namespace std;
const int N = 1e6 + 10;
int n;
int arr[N];
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i++; while (q[i] < x);
do j--; while (q[j] > x);
//上面两行do while 执行完后,相当于i指向>=x,j指向<=x
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
quick_sort(arr, 1, n );
for (int i = 1; i <= n; i++)
{
cout << arr[i] << " ";
}
return 0;
}
题目
#include
using namespace std;
int n;
const int N=1e6+10;
int arr[N];
int tmp[N];
void merge_sort(int arr[],int left,int right)
{
if(left==right) return;//截至条件
int mid=left+right >> 1;
//递归,一直递归到mid-left=1
merge_sort(arr,left,mid);
merge_sort(arr,mid+1,right);
//合并
int i=left,j=mid+1, k=0;
while(i<=mid&&j<=right)
{
if(arr[i]<=arr[j]) tmp[k++]=arr[i++];
else tmp[k++]=arr[j++];
}
while(i<=mid) tmp[k++]=arr[i++];
while(j<=right) tmp[k++]=arr[j++];
//tmp赋值给原来的数组arr
for (i = left, j = 0; i <= right; i ++, j ++ ) arr[i] = tmp[j];
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
merge_sort(arr, 0, n-1 );
for (int i =0; i <n; i++)
{
cout << arr[i] << " ";
}
return 0;
}
下标为i的节点的父节点下标:iParent(i) = (i - 1) / 2;[整数除法]
下标为i的节点的左孩子节点下标:iLeftChild(i) = 2 * i + 1;
下标为i的节点的右孩子节点下标:iRightChild(i) = 2 * i + 2;
//每调用一次shift_down就是比较一个节点(父节点)和他的子节点们(1个或者2个)的大小,从最后一个节点的父节点开始比较
void shift_down(int arr[], int start, int end)
{
// 计算父结点和子结点的下标
int parent = start;//假设start为父节点
int child = parent * 2 + 1;//child是左孩子,child+1是右孩子
while (child <= end) // 子结点下标在范围内才做比较
{
// 先比较两个子结点大小,选择最大的
if (child + 1 <= end && arr[child] < arr[child + 1])
{
child++;
}
// 如果父结点比子结点大,代表调整完毕,直接跳出函数
if (arr[parent] >= arr[child])
return;
else
{ // 否则交换父子内容,子结点再和孙结点比较
swap(arr[parent], arr[child]);
parent = child;
child = parent * 2 + 1;//左孙节点
}
}
}
void heap_sort(int arr[], int len)
{
// 从最后一个节点的父节点开始 sift down 以完成堆化 (heapify)
//最后一个节点是len-1,他的父节点是(len-1-1)/2
for (int i = (len - 1 - 1) / 2; i >= 0; i--)
{
shift_down(arr, i, len - 1);//把最大的元素放到顶部
}
// 先将第一个元素和已经排好的元素前一位做交换,再重新调整(刚调整的元素之前的元素),直到排序完毕
for (int i = len - 1; i > 0; i--)
{
swap(arr[0], arr[i]);
sift_down(arr, 0, i - 1);
}
}
int main()
{
int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
int len = arr.size();
//int len = (int) sizeof(arr) / sizeof(*arr);
heap_sort(arr, len);
for (int i = 0; i < len; i++)
cout << arr[i] << " ";
cout << endl;
return 0;
}
const int N = 100010;
int n, w, a[N];
vector<int> bucket[N];
//对每个桶里面的数使用插入排序
void insertion_sort(vector<int>& A)
{
for (int i = 1; i < A.size(); ++i)
{
int key = A[i];
int j = i - 1;
while (j >= 0 && A[j] > key)
{
A[j + 1] = A[j];
--j;
}
A[j + 1] = key;
}
}
void bucket_sort()
{
int bucket_size = w / n + 1;//桶的大小,n是桶的个数,w是要排序的数有多少个
for (int i = 0; i < n; ++i)
{
bucket[i].clear();
}
for (int i = 1; i <= n; ++i)
{
bucket[a[i] / bucket_size].push_back(a[i]);
}
int p = 0;
for (int i = 0; i < n; ++i)
{
insertion_sort(bucket[i]);
for (int j = 0; j < bucket[i].size(); ++j)
{
a[++p] = bucket[i][j];//a是最后排好序的数组
}
}
}
int maxbit(int data[], int n) //辅助函数,求数据的最大位数,比如说最大数是3356,那么最大位数是4
{
//int maxData = data[0]; ///< 最大数
/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
//for (int i = 1; i < n; ++i)
//{
//if (maxData < data[i]) maxData = data[i];
//}
int maxData = *max_element(data.begin(), data.end());
int d = 1;
int p = 10;
while (maxData >= p)
{
maxData /= 10;
++d;
}
return d;//d是最大位数
//另外一种求最大位数的方法,不用求最大值maxData,直接遍历每个数,就是可能会overflow
/* int d = 1; //保存最大的位数
int p = 10;
for(int i = 0; i < n; ++i)
{
while(data[i] >= p)
{
p *= 10;
++d;
}
}
return d;*/
}
void radixsort(int data[], int n) //基数排序
{
int d = maxbit(data, n);
int *tmp = new int[n];
int *count = new int[10]; //计数器
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) //进行d次排序
{
for(j = 0; j < 10; j++)
count[j] = 0; //每次分配前清空计数器
for(j = 0; j < n; j++)
{
k = (data[j] / radix) % 10; //统计每个桶中的记录数
count[k]++;
}
for(j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
{
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
for(j = 0; j < n; j++) //将临时数组的内容复制到data中
data[j] = tmp[j];
radix = radix * 10;
}
delete []tmp;
delete []count;
}
补充:C++中 new int[] 和 new int() 的区别
(1)int *p = new int[3];//申请一个动态整型数组,数组的长度为3。 value: p[i];addr: &p[i]
(2)int *p = new int(3); // p指向一个值为3的int数。value: *q;addr: q
数据结构 | 空间 | |
---|---|---|
DFS | stack | O ( h ) O(h) O(h)不具有最短路性质 |
BFS | queue | O ( 2 h ) O(2^h) O(2h) 具有最短路性质 |
h是高度,所以DFS用的空间比BFS小。
所谓深度优先,就是说每次都尝试向更深的节点走。DFS包含概念回溯和剪枝。每个DFS都对应一个搜索树。
#include
using namespace std;
const int N=10;
int n;//总共有多少层
int path[N];//记录方案
bool st[N];//标记数组 哪些数被用过了,true代表这个数被用过了 一开始默认全为0也就是false
void dfs(int u)//u代表在第几层了,u=0在第一层,u=1在第二层.....
{
if(u==n)//叶节点
{
for(int i=0;i<n;i++) cout<<path[i]<<" ";
cout<<endl;
return;//直接退出这个dfs函数,到上一个dfs函数
}
for(int i=1;i<=n;i++)
{
if(!st[i])
{
path[u]=i;
st[i]=true;//代表点i已经被遍历过
dfs(u+1);
//到底部时,回溯+恢复现场
//path[u]=0;
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(0);//一开始在第0层
return 0;
}
题目 难度:中等
给定一个不含重复数字的数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。注意如果要求按照字典顺序输出,就必须使用标记数组。
class Solution {
public:
vector<int> path;
vector<vector<int>> result;
void dfs(vector<bool>& st,vector<int>& nums)
{
if(path.size()==nums.size())
{
result.push_back(path);
return;
}
for(int i=0;i<nums.size();i++)
{
if(st[i]==false)
{
path.push_back(nums[i]);
st[i]=true;
dfs(st,nums);
//回溯,恢复现场
path.pop_back();
st[i]=false;
}
else if(st[i]==true) continue;//就行下一个for循环
}
}
vector<vector<int>> permute(vector<int>& nums)
{
vector<bool> st(nums.size(), false);//标记数组st,一开始默认都是false
result.clear();
path.clear();
//使用DFS方法
dfs(st,nums);
return result;
}
};
这题的标记数组就不止一个了,有三个:(1)col[ ]代表列,(2)dg[ ]代表副对角线,(3)udg[ ]代表主对角线。
斜角判断法:
我们可以对4x4正方形进行一个枚举:
(0,0) (0,1) (0,2) (0,3)
(1,0) (1,1) (1,2) (1,3)
(2,0) (2,1) (2,2) (2,3)
(3,0) (3,1) (3,2) (3,3)
坐标可以理解为row和i (row是第几行,i是第几列)。
(1)每条从右上到左下的对角线(副对角线),i+row的值也列出来。
(0) (1) (2) (3)
(1) (2) (3) (4)
(2) (3) (4) (5)
(3) (4) (5) (6)
(4) (5) (6) (7)
横纵坐标相加的值都是相等的,这条对角线上的点都可以通过row+i(也就是x+y),也就是横纵坐标的和来表示这个dg[row + i]是否为true。
(2)从左上角到右下角的对角线(主对角线),n+i-row的值也列出来。
(4) (5) (6) (7)
(3) (4) (5) (6)
(2) (3) (4) (5)
(1) (2) (3) (4)
横纵坐标相减都等于一个值i-row(也就是y-x),那为何这里要加上n呢,因为i-row的值有的为负,但是数组下标不可为负,所以加上n。那么这条对角线上的点都可以通过n+i-row(也就是n+y-x),来表示这个udg[n+i-row]是否为true。
#include
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool col[N], dg[N], udg[N];//一开始默认为false,即没被用过
void dfs(int row)//表示第row行皇后放在哪
{
if (row == n)
{
for (int i = 0; i < n; i ++ ) puts(g[i]);
puts("");
return;
}
for (int i = 0; i < n; i ++ )第row行的皇后可以放在第i列吗
if (!col[i] && !dg[row + i] && !udg[ - row + i])
{
g[row][i] = 'Q';
col[i] = dg[row + i] = udg[ - row + i] = true;//表示用过
dfs(row + 1);
//回溯
col[i] = dg[row + i] = udg[ - row + i] = false;
g[row][i] = '.';
}
}
int main()
{
cin >> n;
for (int i = 0; i <n; i ++ )
for (int j = 0; j < n; j ++ )
g[i][j] = '.';
dfs(0);
return 0;
}
倘若不想用标记矩阵,可以使用一个check函数:check(int x,int y),检查一下在第x行上,能不能把皇后放在第y列上面,但也是使用了斜角判断法。
#include
using namespace std;
int n;
const int N=10;
int a[N];//a[i]表示第i行的皇后放在第a[i]列上
char g[N][N];
bool check(int x,int y)//第x行上,能不能把皇后放在第y列上面
{
for(int i=0;i<x;i++)//在前面的行上面,有没有同一列或同一斜线上有皇后
{
if(a[i]==y) return false;
if(i+a[i]==x+y) return false;
if(i-a[i]==x-y) return false;
}
return true;
}
void dfs(int row)//表示第row行皇后放在哪
{
if(row==n)//产生了一组解
{
for(int i=0;i<n;i++) cout<<g[i]<<endl;
puts("");
return;
}
for(int i=0;i<n;i++)
{
if(check(row,i))//第row行的皇后可以放在第i列吗
{
a[row]=i;
g[row][i]='Q';
dfs(row+1);
//回溯
a[row]=0;
g[row][i]='.';
}
}
}
int main()
{
cin>>n;//有n个皇后
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g[i][j]='.';
}
}
dfs(0);
return 0;
}
所谓广度优先,就是每次都尝试访问同一层的节点。 如果同一层都访问完了,再访问下一层。
题目 难度:中等
#include
#include
using namespace std;
const int N=110; //n,m的数据范围均为100
int maze[N][N],dis[N][N],n,m; //maze[N][N]用来存储迷宫//dis[x][y]用来存储(x,y)这一点到坐标原点的距离
queue <pair<int,int>> q;//q队列用来存储宽度优先搜素到的路径也就是走迷宫经过哪些点
int bfs()
{
memset(dis,-1,sizeof dis); //将dis数组所有元素初始化为-1,表示这个点没有走过
dis[0][0]=0; //位于原点(0,0)到原点的距离为0
q.push({0,0}); //将原点入队
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; //定义方向向量一共四个方向
while(!q.empty()) //当队列非空时执行循环
{
auto t=q.front();//取出第一个点
q.pop(); //插入一个位置的同时会弹出一个位置保证循环可以正常终止
for(int i=0;i<4;i++) //遍历四个方向
{
int x=t.first+dx[i],y=t.second+dy[i]; //四个方向对应x,y坐标
if(x>=0 && x<n && y<m && y>=0 && maze[x][y]==0 && dis[x][y]==-1 )//x,y都要在迷宫坐标范围内,并且(x,y)不是墙壁且没被走过
{
dis[x][y]=dis[t.first][t.second]+1; //走到下一个点的同时距离加1
q.push({x,y}); //将该点入队尾
}
}
}
return dis[n-1][m-1];//dis[n-1][m-1]是指坐标为(n-1,m-1)的点距离坐标(0,0)的距离
}
int main()
{
cin>>n>>m; //输入迷宫的尺寸大小
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>maze[i][j]; //输入迷宫,比如输入maze[1][1]=0,maze[1][2]=1
}
}
cout<<bfs()<<endl; //输出宽度优先搜索结果
return 0;
}