一、基础算法
1.前缀与拆分
PS:计算数组从l到r的项数和,即s[r]-s[l-1],s[n]为a[n]前n项和。
#include<stdio.h>
int main(){
int a[7],s[7]={0};
int l,r,i;//l,r代表从l到n,都大于1 且l<r
for( i=1;i<=6;i++)
scanf("%d",&a[i]);
scanf("%d %d",&l,&r);
for( i=1;i<=6;i++)
s[i]=s[i-1]+a[i];
for( i=1;i<=6;i++)
printf("%d ",s[i]);
printf("\n%d",s[r]-s[l-1]);
return 0;
}
2.高精度加法
PS:定义三个字符数组a,b,分别用来存储两个加数,定义一个temp来存储进位,且初始化为0,定义一个整数数组存储和。
字符数组转换成整数数组A,B。
A[0]是个位,A[1]是十位,以此类推b(B数组也是如此)。
个位相加即A[0]+B[0]+temp(初始化为0不会影响)=C[0],如果有进位(和是两位数),则把进位(和的十位)存储在temp中,个位存储在C[0]。
十位相加即A[0]+B[0]+temp=C[0],如果有进位(和是两位数),则把进位(和的十位)存储在temp中,个位存储在C[0]。
一直加下去,得出结果。
#include <iostream>
#include <cstring>
using namespace std;
int A[100000],B[100000],C[10000000],temp=0,lena,lenb,lenc;
char a[1000000],b[10000000];//因为数组太大尽量都定义在main函数外面。
int main(){
cin>>a;
cin>>b;
lena=strlen(a) ;//计算长度
lenb=strlen(b);
for(int i=0;i<lena;++i)
A[i]=a[lena-1-i]-'0';
/*
将字符数组变成整数数组,并且将数组倒转。
为什么要倒转呢?
因为一开始输入时,高位在前,只有倒转以后才可以做到低位在前 。
例如输入100,200;
在a,b中存储时100,200;
倒转以后在A,B中就时001,002;
方便计算
*/
for(int i=0;i<lenb;++i)
B[i]=b[lenb-1-i]-'0';
lenc=lena>lenb?lena:lenb;//得到较长的一个数
for(int i=0;i<lenc;++i)
{
C[i]=A[i]+B[i]+temp;
temp=C[i]/10;
C[i]%=10;
}
if(temp!=0)//看最后是否temp为0。并进行分类处理
{
C[lenc]=temp;
for(int i=lenc;i>=0;--i)
cout<<C[i];
return 0;
}
for(int i=lenc-1;i>=0;--i)//先输出高位 c
cout<<C[i];
return 0;
}
3.高精度乘法
PS:
分析c数组下标的变化规律,可以写出如下关系式:ci = c’i +c”i +…由此可见,c i跟
a[i]*b[j]
乘积有关,跟上次的进位有关,还跟原c i的值有关,分析下标规律,有c[i+j-1]= a[i]*b[j]+ x + c[i+j-1]; x=c[i+j-1]/10 ; c[i+j-1]%=10;
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char a1[100],b1[100];
int a[100],b[100],c[100],lena,lenb,lenc,i,j,x;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
gets(a1);gets(b1);
lena=strlen(a1);lenb=strlen(b1);
for (i=0;i<=lena-1;i++) a[lena-i]=a1[i]-48;
for (i=0;i<=lenb-1;i++) b[lenb-i]=b1[i]-48;
for (i=1;i<=lena;i++)
{
x=0; //用于存放进位
for (j=1;j<=lenb;j++) //对乘数的每一位进行处理
{
c[i+j-1]=a[i]*b[j]+x+c[i+j-1]; //当前乘积+上次乘积进位+原数 x=c[i+j-1]/10;
c[i+j-1] %= 10;
}
c[i+lenb]=x; //进位
}
lenc=lena+lenb;
while (c[lenc]==0&&lenc>1) //删除前导0
lenc--;
for (i=lenc;i>=1;i--)
cout<<c[i];
cout<<endl;
return 0;
}
4.二分查找法
PS:二分法最重要的两个点,就是循环条件和后续的区间赋值问题。以下是左闭右闭型。
左闭右开则相反。
int search(int nums[], int size, int target) //nums是数组,size是数组的大小,target是需要查找的值
{
int left = 0;
int right = size - 1; // 定义了target在左闭右闭的区间内,[left, right]
while (left <= right) { //当left == right时,区间[left, right]仍然有效
int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出
if (nums[middle] > target) {
right = middle - 1; //target在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; //target在右区间,所以[middle + 1, right]
} else { //既不在左边,也不在右边,那就是找到答案了
return middle;
}
}
//没有找到目标值
return -1;
}
5.快速排序法
原理:
先选择一个数作为 基准值 (这里用的是 第一个数),进行一次排序
然后将所有比'基准值小的数'放在基准值的'左边',
将所有比'基准值大的数'放在基准值的'右边',然后再对两边的,各自'再取一个数作为基准值',然后再次排序(递归[自己调自己])。
思想:
通过一个基准值,把一组数据分成两个部分(一边小,一边大),然后,在对两个部分,分别找一个基准值,再次进行排序,直至每一个小部分不可再分,所得到的整个数组就成为了有序数组。
#include <stdio.h>
#include <stdlib.h>
#define N 10
//快速排序法
int quick_sort(int *a, int low, int high)
{
int i = low; //第一位
int j = high; //最后一位
int key = a[i]; //将第一个数作为基准值-- 先找到一个基准值
//进行排序---> 最终结果就是 左面的 都比基准值小 ,右面的都比 基准值大,所以这是所有循环的结束条件
while (i < j)
{
//下面的循环执行的条件是 如果右面的比基准值大,就赋一下值,否则继续向前移动
//---如果直接把循环写成下面这样---
//while(a[j] >= key) //如果下面的不写这个i<j,这个就出错、越界,并且排序不准--理由:
//如果i<j,并且: 右面的值 大于 基准值 时,j往前移动一个
//i 跟 j 的可能情况 只有 i<j i==j
while(i < j && a[j] >= key)//i<j 是 当前while循环的结束条件,如果没有这个,i会大于j,出现越界,错误
{
j--;//继续走
}//如果不成立,也就是 a[j] <= key;右面的比key小了,那就换个位置
//把a[j]的数据给a[i]
a[i] = a[j];
//将事先保存好的基准值与左边的值进行比较,如果基准值大,保持不变,i往前
//然后 判断一下这个新的a[i],也就是之前的a[j]跟key值的关系---> 一定是 a[i]<key
//所以把i向前移动一下,i++
while(i < j && a[i] <= key)
{
i++;
}
//移动完以后,把新的位置的a[i]的数值 给刚才的 a[j],然后开始下一次循环
a[j] = a[i];
}
//跳出循环,将基准值放入数据a[i]中
a[i] = key;
//对基准值左边 的所有数据 再次进行快速查找(递归)
if (i-1 > low)
{
quick_sort(a, low, i-1);
}
//对基准值右边的所有数据再次进行快速查找(递归)
if (i+1 < high)
{
quick_sort(a, i+1, high);
}
return 0;
}
int main(int argc, const char *argv[])
{
int a[N] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};//先整了个数组,初始化了一堆数
int i = 0;
printf("排序前:\n");
for(i = 0; i < N; i++)
{
printf("%d ", a[i]);
}
putchar(10);
//调用-快排
quick_sort(a, 0, N-1);//数组,0 ,9
printf("排序后:\n");
for(i = 0; i < N; i++)
{
printf("%d ", a[i]);
}
putchar(10);
return 0;
}
6.归并排序法
算法思路:归并排序算法有两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。
第一步,将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。
第二步,将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。
void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex){
int i = startIndex, j=midIndex+1, k = startIndex;
while(i!=midIndex+1 && j!=endIndex+1) {
if(sourceArr[i] > sourceArr[j])
tempArr[k++] = sourceArr[j++];
else
tempArr[k++] = sourceArr[i++];
}
while(i != midIndex+1)
tempArr[k++] = sourceArr[i++];
while(j != endIndex+1)
tempArr[k++] = sourceArr[j++];
for(i=startIndex; i<=endIndex; i++)
sourceArr[i] = tempArr[i];
}
//内部使用递归
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex) {
int midIndex;
if(startIndex < endIndex) {
midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出int
MergeSort(sourceArr, tempArr, startIndex, midIndex);
MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);
}
}
int main(int argc, char * argv[]) {
int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};
int i, b[8];
MergeSort(a, b, 0, 7);
for(i=0; i<8; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
7.删除重复元素
算法思想:
等同于设置快慢指针,快指针寻找与慢指针所指元素不等的元素,慢指针扫描整个顺序表,一旦找到不相等的元素,慢指针就向前走一步,并且赋值为快指针所指向元素。这样就可以保证, 慢指针每向前走一步所被赋值的元素都是不重复的,一遍下来,就可以删除所有重复的元素。
void DeleteSimple(SeqList &L){
int i,j;
for(i=0, j=1; j<L.length; j++)
if(L.data[i]!=L.data[j])
L.data[++i] = L.data[j];
L.length = i+1;
}