牛客网编程题常见的编译错误:
(1)常常有逻辑是对的,但是打印时没有输出结果的情况
原因:一般是输入的测试数据有多组,但编写的程序中没有使用循环接收输入数据,直接收了一组测试数据造成的;
(2)对于二叉树等类似题型,提示堆栈溢出,递归或循环超出范围的情况
原因:一般是首次进入树序列时没有判断树的根节点是否为空;
答:描述算法的方法有多种,常用的有自然语言、结构化流程图、伪代码和PAD图等,其中最普遍的是流程图。
算法描述:
自然语言 也就是文字描述;
流程图 特定的表示算法的图形符号;
伪语言 包括程序设计语言的三大基本结构及自然语言的一种语言;
类语言 类似高级语言的语言,例如,类PASCAL、类C语言。
注:基数排序的时间复杂度一般也可表示为O(r*n),当r较小时就近似为O(n);
如上表所示:
(1)冒泡、选择、直接插入排序统称为简单排序,时间复杂度均为O(n^2);
(2)希尔排序的时间复杂度与比较步长有关,一般可认为O(n^1.3);
(3)选择排序和堆排序的时间复杂度与初始序列排列顺序无关;
(4)空间复杂度有3个不是o(1),分别是快、归、基,其中快排又是相对较小的。
(5)比较排序的时间复杂度最多可以减少到O(nlogn),基数排序不是比较类排序,所以可以做到O(n);
答:①就是对一段数据序列,从第一个开始像摸扑克牌时那样插入排序,如下图:
void insertion_sort(vector &v) //插入排序算法
{
int temp = 0;
for (int i = 1; i < v.size(); i++)
{
if (v[i - 1] > v[i])
{
temp = v[i];
for (int j=i-1; j > =0 && v[j] > temp; j--)
{
v[j+1] = v[j];
}
v[j+1] = temp;
}
}
}
②直接插入排序的优化:在查找插入位置时使用二分查找法,因为前面都是有序序列,使用二分查找法速度更快;
如上图,初始时,有一个大小为 10 的无序序列。
在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。
接下来,按照直接插入排序的方法对每个组进行排序。
在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2 的元素组成一组,可以分为 2 组。
按照直接插入排序的方法对每个组进行排序。
在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组。
按照直接插入排序的方法对每个组进行排序。此时,排序已经结束。
需要注意一下的是,图中有两个相等数值的元素 5 和 5 。我们可以清楚的看到,在排序过程中,两个元素位置交换了。
所以,希尔排序是不稳定的算法。时间复杂度:希尔排序的时间复杂度跟比较步长的选择有关,一般可以做到O(n^1.3);
代码实现:
void ShellInsert(vector &v, int step)//比较希尔排序代码与简单插入排序代码的异同
{
int temp=0;
for(int i=step; i=0 && v[j]>temp; j=j-step)
{
v[j+1]=v[j];
}
v[j+1]=temp;
}
}
}
void ShellSort(vector v, vector step)//最终排序
{
//按增量数组step依次对序列做希尔排序
for(int i=0; i
答:①冒泡排序是数据前后相邻数据比较,前比后大则两者交换,之后继续向后推进比较,一轮下来最大的数据会出现在序列最后。普通的冒泡排序时间复杂度始终是n^2。实现代码略;
②针对冒泡排序的改进,即某一趟冒泡排序后没有任何元素交换位置,则结束排序——设标志位。改进部分如下:
void(vector v)
{
for(int i=0; i v[j+1])
{
swap(v[j],v[j+1]);
flag=true;//有交换就将标志位置位
}
}
if(!flag)//若一趟结束都没有一次交换,表示序列已经有序
break;
}
}
注意:快速排序中最快速情况是:每一趟排序的基准值(一般第一个数据)都可以在一趟排序完成后,将当前序列平均分为两个个数相等的序列。最差的情况是,每次选取的基准数据都是当前序列中最小或最大值,即当前序列是有序序列,此时其会退化为冒泡排序。简单总结:快排相比其他排序算法最具优势的情况是数值序列完全无序,最不具优势的情况是数值序列基本有序;
代码:
void quicksort(vector &v,int left, int right)
{
if(left < right)//false则递归结束
{
int key=v[left];//基数赋值
int low = left;
int high = right;
while(low < high) //当low=high时,表示一轮分割结束
{
while(low < high && v[high] >= key)//v[low]为基数,从后向前与基数比较
{
high--;
}
swap(v[low],v[high]);
while(low < high && v[low] <= key)//v[high]为基数,从前向后与基数比较
{
low++;
}
swap(v[low],v[high]);
}
//分割后,对每一分段重复上述操作
quicksort(v,left,low-1);
quicksort(v,low+1,right);
}
}
快排更多详情参考:http://blog.csdn.net/xiongchao99/article/details/74524807#t3
①每一次都遍历数据序列,从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在被排序序列第一个位置,直到全部待排序的数据元素排完(第一轮:用第一个数与后面数据比较,后面小就与之交换位置,继续用交换后的第一个和后续数据比较……,第二轮:用第二个数据和后面比较,类似第一轮方式,……,直到比较完所有数据)。 选择排序是不稳定的排序方法。总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2,与数据的初始排列顺序无关。实现代码略。
②除此之外还有树形选择排序,即锦标赛排序。如一个数据序列中找出最大的和第二大的数,用竞标赛思想解决最好:如有序列ABCDEFGH共8个数据的序列,找出最大和第二大的两个,需要比较的次数,见下图:
注意:使用的最小/最大堆都是完全二叉树;
首先可以看到堆建好之后堆中第0个数据是堆中最小的数据。取出这个数据,再根据章节二中数据结构的堆删除方式,执行下堆的删除操作并进行堆恢复工作。这样堆中第0个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。
由于堆也是用数组模拟的,故堆化数组后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面排好序的数据序列前面,故操作完成后整个数组就有序了。注意使用最小堆排序后是递减数组,反之,最大堆排序后是递增数组。
堆顶删除后的排序也可见下例:
//注意:为了计算方便,默认数组从下标1开始,即数组v的首元素空着;
void HeapAdjust(vector v, int start, int end)//start是待调整堆的堆顶元素下标,end即为待调整堆的最后一个元素下标
{
int top=v[start];
for(int j=2*start; j<=end; j=2*j)
{
if(j &v) //最终排序
{
//建堆(从下往上进行子堆调整即可实现)
for(int i=v.size()/2; i>0; i--) //i=v.size()/2是倒数第二层的最后一个非叶子节点
HeapAdjust(v, i, v.size()); //本来end应该是当前子堆的最后一个元素,但使用整堆的最后一个元素下标依然可以;
//取堆顶元素与堆末尾交换并调整剩下的前半部分堆元素
for(int j=v.size(); j>=1; j--)
{
swap(v[1], v[j]);
HeapAdjust(v, 1, j-1);
}
}
(2)除此之外,C++的STL中也有建堆和堆调整的函数可调用,有make_heap()。用STL函数实现堆排序具体方式如下:
①代码简写:
vector v;
make_heap(v.begin(),v.end());//建堆
for(;;){
……//交换堆顶和堆尾
make_heap(v.begin(),v.end()-i);//堆调整(用建堆函数实现)
}
②用pop_heap()/push_heap()可以实现堆顶删除调整和堆尾插入调整:
vector v;
pop_heap(v.begin(),v.end());//先pop_heap,然后在容器中删除
v.pop_back();
v.push_back(temp);//先在容器中加入,再push_heap
push_heap(v.begin(),v.end());
堆的删除和插入的语句顺序必须如上述一样:
删除堆顶pop_heap实际并没有删除,只是将堆顶元素放到堆尾,然后对前面剩下的对元素进行堆调整。要实实在在的删除后面就还需要调用pop_back();
插入元素push_heap则要注意必须在push_back语句操作后面,否则无法实现堆调整。
(3)top K问题一般可以使用的算法有堆排序、快排、选择排序。
其中,堆排序用的比较多,因为对于大量数据,可实现NlogK的实现复杂度。其中,具体的可以逐个元素的对堆进行删除/插入,遍历完所有元素后得到的堆元素就是TOP K。
注意:最大的K个元素用最小堆(小根堆),相反最小的K个元素用最大堆(大根堆);
这里给出两种实现最大K个元素的代码:①make_heap调整堆:
vector GetLeastNumbers_Solution(vector input, int k){
int len=input.size();
if(len<=0||k<=0||k>len)
return vector();
vector v(input.begin(),input.begin()+k);//用测试数组前k个元素初始化堆数组
make_heap(v.begin(),v.end());//默认最大堆,要建立最小堆可以添加第三个参数greater()
for(int i=k;iv[0]){ //逐个替换堆顶并调整
v.push_back(input[i]);
swap(v[0],v[k]);
v.pop_back();
make_heap(v.begin(),v.end());
}
}
return v;
}
②删除/插入调整堆(注意pop_heap和pop_back以及push_heap和push_back的顺序):
vector GetLeastNumbers_Solution(vector input, int k) {
int len=input.size();
if(len<=0||k<=0||k>len)
return vector();
vector v(input.begin(),input.begin()+k);
make_heap(v.begin(),v.end());
for(int i=k;i
//归并两个子序列
void Merge(vector v1, vector &v2, int start, int mid, int end) //用一个序列装两组数组,使用start、mid、end区分不同序列
{
//将分组v1[start,...., mid]与分组v1[mid+1, ... ,end]归并为一个数组序列
int i=start, j=mid+1,k=0;
while(i<= mid && j<=end )
{
if(v1[i] v2; //创建辅助数组(最终长度为n)
void MergeSort(vector &arr, int start,int end)
{
if(start
void mergeSort(vector& data, int start, int end) {
// 递归终止条件
if(start >= end) {
return 0;
}
// 递归
int mid = (start + end) / 2;
mergeSort(data, start, mid);
mergeSort(data, mid+1, end);
// 归并排序,并计算本次逆序对数
vector copy(data); // 数组副本,用于归并排序
int foreIdx = mid;// 前半部分的指标
int backIdx = end;// 后半部分的指标
int counts = 0;// 记录本次逆序对数
int idxCopy = end;// 辅助数组的下标
while(foreIdx>=start && backIdx >= mid+1) {
if(data[foreIdx] > data[backIdx])
copy[idxCopy--] = data[foreIdx--];
else
copy[idxCopy--] = data[backIdx--];
}
while(foreIdx >= start) {
copy[idxCopy--] = data[foreIdx--];
}
while(backIdx >= mid+1) {
copy[idxCopy--] = data[backIdx--];
}
for(int i=start; i<=end; i++) {
data[i] = copy[i];
}
}
答:要求:
1.必须采用顺序存储结构;
2.必须按关键字大小有序排列(不一定要升序)。
方法:取正中间进行比较,小则丢掉正中间被比较数大的一侧所有数据,继续采用二分查找比较小的一侧数据。查找中,偶数个数据取正中间靠近起始方向的数据比较,奇数个数据取正中间的。
时间复杂度:o(lgN)
答:n只蚂蚁以每秒1cm的速度在长为Lcm的竹竿上爬行。当蚂蚁看到竿子的端点时就会落下来。由于竿子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自反方向爬行。对于每只蚂蚁,我们只知道它离竿子最左端的距离为xi,但不知道它当前的朝向。请计算所有蚂蚁落下竿子的最短时间和最长时间。
问题的要点:蚂蚁相遇后反方向爬行当做穿透对方继续爬行。故最大时间就是离某一端点最远的蚂蚁用时,最小时间则为离某端点最近的蚂蚁用时中的最大者。
问题:汉诺塔问题中有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
①每次只能移动一个圆盘;
②每个杆上大盘不能叠在小盘上面;
③根据A上原有圆盘个数k,要完成A上所有圆盘移出至C需要移动次数为。
汉诺塔移动次数的公式:f(k+1)=2*f(k)+1;(其中,f⑴=1,f⑵=3,f⑶=7)
为什么呢?假设有4个圆盘,那么移动过程可以如下描述:
①中其中将1,2,3号移动到B,移动次数为f(3);
②中只是将最底下的圆盘移动到空杆C上,那么移动次数就是1;
③中将A作为辅助,移动B上的1,2,3号到C上,移动次数同①,仍然为f(3);
故总次数为:f(4)=2*f(3)+1;
以上移动流程具有普适性,可以推广到k=n,故可得公式:f(k+1)=2*f(k)+1。
答:电梯调度算法:
1)电梯有移动方向,各楼层的请求有请求方向,这里维护一个请求表(记录请求ID,请求方向,该请求的停靠楼层);
2)电梯按照一个方向移动,直到该方向没有请求,不会根据某一层的请求方向突然改变电梯的移动方向。但是注意:电梯在移动过程中只处理与“电梯移动方向”相同请求方向的请求。如电梯向下移动,只处理电梯下方楼层的请求,且该请求的方向也向下(停靠楼层请求无方向)。若请求楼层在向下方向,但请求方向不是向下,是不做处理的;
3)没完成一个请求,就从请求表中删除该请求记录;
4)若移动方向上已经没有请求(这个请求不仅包括请求表中的请求楼层,还包括停靠楼层),但电梯移动方向的反方向有请求,就把电梯移动方向置位为反方向;
实际上,电梯调度算法和一些操作系统调度算法如磁盘寻道是类似的。
请看下面例子:
for(int i=1;i<=N;i++)
{
for(int j=1;j<=M;j++)
{
if(v[i]<=j)
{
dp[i][j] = dp[i-1][j-v[i]]+v[i]>dp[i-1][j] ? dp[i-1][j-v[i]]+v[i]:dp[i-1][j];
}
else
dp[i][j]=dp[i-1][j];
}
}
其中,dp[][]数组存储的就是价值,i是物件编号,j是允许的最大重量,v是单件价值;
for (int i=1; i<=N; i++)
for (int j=M; j>=1; j--)
{
if (weight[i]<=j)
{
f[j]=max(f[j],f[j-weight[i]]+value[i]); //被修改的f[j]这一轮循环后续部分就不会再用了,所以直接用一个数组即可
}
}
for (int i=1; i<=N; i++)
for (int j=1; j<=M; j++)
{
for(int k=1;k
for (int i=1; i<=N; i++)
for (int j=M; j>=1; j--)
{
if (weight[i]<=j)
{
f[j]=max(f[j],f[j-weight[i]]+value[i]);
}
}
if((m_a+m_b>m_c) && (m_a+m_c>m_b) && (m_b+m_c>m_a)) //这个判断一定需要有
{
double s=(m_a+m_b+m_c)/2; //算法主要部分
double area=sqrt(s*(s-m_a)*(s-m_b)*(s-m_c));
}
int maxLen2(string str)
{
string s;
//添加辅助符#
s.push_back('#');
for(int k=0;k=0&&i+count<=len-1 && s[i-count]==s[i+count]){
count++;
}
if(maxlen
int maxLen3(string str)
{
string s;
//添加辅助符#
s.push_back('#');
for(int k=0;ki)
{
p[i]=min(mx-i+1,p[2*pi-i]);//核心
}else{
p[i]=1;
}
while(i-p[i]>=0&&i+p[i]<=len-1 && s[i-p[i]]==s[i+p[i]]){
p[i]++;
}
if(i+p[i]-1 > mx){
mx = i+p[i]-1;
pi = i;
}
}
//最大回文字符串长度
int maxlen = 0;
for(int i=1;imaxlen)
{
maxlen = p[i];
}
}
delete []p;
return maxlen-1;
}
#include
#include
#include
#include
using namespace std;
int maxLen(string s1, string s2){
int length1 = s1.size();
int length2 = s2.size();
vector > MaxLen(length1+1,vector(length2+1)); //也可以用指针来定义二维动态数组
for (int i = 1; i <= length1; ++i)
{
for (int j = 1; j <= length2; ++j)
{
if (s1[i-1] == s2[j-1]){
MaxLen[i][j] = MaxLen[i-1][j - 1] + 1;
}
else{
MaxLen[i][j] = max(MaxLen[i - 1][j], MaxLen[i][j - 1]);
}
}
}
return MaxLen[length1][length2];
}
int main(){
string str;
while(cin>>str){
string str0=str;
reverse(str.begin(),str.end());
cout<
void getPrime0(int n){
int i,j;
bool m;
for(i = 1; i <= n; i ++){
m = true;
for(j = 2; j < i; j ++){
if(i % j == 0){
m = false;
break;
}
}
if(m){
cout << i << " ";
}
}
cout << endl;
}
bool prime(int x)
{
int y;
for(y=2;y<=sqrt(x);y++)
if (x%y==0)
return false;
return true;
}
以上是使用开根号算法的素数判断函数。
int climbStairs(int n){
vector v;
v.push_back(1);
v.push_back(1);
for(int i = 2; i <= n; i++){
v.push_back(v[i - 1] + v[i - 2]);
}
return v[n];
}
(2)斐波那契查找
答:n个数有多少种出栈序列,用卡特兰数求:
f(n)=f(0)f(n-1)+f(1)f(n-2)+f(2)f(n-3)+……+f(n-2)f(1)+f(n-1)f(0);其中,f(0)=f(1)=1;
所以,如果有一入栈序列为e1,e2,e3,e4,e5,那么出栈序列就有f(5)=42种。
答:快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2,慢指针每次向前移动1次。
(1)快慢指针可以用于判断单循环链表:让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置:
1)如果快指针到达NULL,说明链表以NULL为结尾,不是循环链表;
2)如果 快指针追上慢指针,即快指针=慢指针,则表示出现了循环。
3)为什么慢指针步长为1的话,快指针步长就为2:因为只有fastStep-slowStep=1,才能实现快慢指针一定相遇,而不是快指针越过慢指针。
代码实现如下:
int isExitsLoop(LinkList* L) {
LinkList *fast, *slow;
fast = slow = L;
while (fast!=NULL && fast->next!=NULL)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
{
break;
}
}
return ((fast == NULL) || (fast->next == NULL));
}
快慢指针用于判断有无环,有时候还要判断环的入口位置(尤其是单链表局部环入口),这种情况参考另一博文:http://blog.csdn.net/xiongchao99/article/details/74524807#t15
(2)快慢指针获取链表中间节点
使用快慢指针同时出发,当快指针到达终结点时慢指针刚好到达中间节点。
答:1)若二叉树是二叉排序树(或叫做二叉查找树、二叉搜索树):
直接前序遍历,找到一个节点m,满足n1
2)若为普通二叉树:
①树节点结构体中给定父节点指针
如:
struct node{
Node * left;
Node * right;
Node * parent;
}
则算法思想:首先给出p的父节点p->parent,然后将q的所有父节点依次和p->parent作比较,如果发现两个节点相等,则该节点就是
最近公共祖先,直接将其返回。如果没找到相等节点,则将q的所有父节点依次和p->parent->parent作比较,直到p->parent==root。
程序实现如下:
Node * NearestCommonAncestor(Node * root,Node * p,Node * q)
{
Node * temp;
while(p!=NULL)
{
p=p->parent;
temp=q;
while(temp!=NULL)
{
if(p==temp->parent)
return p;
temp=temp->parent;
}
}
}
②若未给定父节点指针
算法思想:如果一个节点的左子树包含p,q中的一个节点,右子树包含另一个,则这个节点就是p,q的最近公共祖先。
程序实现:
/*查找a,b的最近公共祖先,root为根节点,out为最近公共祖先的指针地址*/
int FindNCA(Node* root, Node* a, Node* b, Node** out)
{
if( root == null )
{
return 0;
}
if( root == a || root == b )
{
return 1;
}
int iLeft = FindNCA(root->left, a, b, out);
if( iLeft == 2 )
{
return 2;
}
int iRight = FindNCA(root->right, a, b, out);
if( iRight == 2 )
{
return 2;
}
if( iLeft + iRight == 2 )
{
*out = root;
}
return iLeft + iRight;
}
用递归方式实现对树一层一层的访问,若left+right=2,那么表示当前节点就是最近祖先节点。
答:例如,实现栈数据的升序排列,即栈顶数据最大。
思路:利用一个辅助栈,每次比较排序栈和辅助栈的顶元素,如果排序栈较小直接压入辅助栈,并弹出排序栈,否则将辅助栈的元素弹出并压在排序栈栈顶元素的后面。如此反复,直到排序栈没有元素了,之后将辅助栈的元素全部导入排序栈,就完成排序。
程序实现:
class TwoStacks {
public:
vector twoStacksSort(vector numbers) {
stack mystack,help;
for(auto i=numbers.end()-1;i>=numbers.begin();--i)
mystack.push(*i);
while(!mystack.empty())
{
if(help.empty()){
help.push(mystack.top());
mystack.pop();
}
else if(mystack.top()<=help.top())
{
help.push(mystack.top());
mystack.pop();
}
else
{
int temp=mystack.top();
mystack.pop();
mystack.push(help.top());
mystack.push(temp);
help.pop();
}
}
while(!help.empty())
{
mystack.push(help.top());
help.pop();
}
for(auto &c:numbers)
{
c=mystack.top();
mystack.pop();
}
return numbers;
}
};
答:一般算法分为两个步骤:
(1)第一步在树A中找到和B的根节点的值一样的结点R;
(2)第二步再判断树A中以R为根结点的子树是不是包含和树B一样的结构。
C++实现代码如下:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool flag=false;
if(pRoot1!=NULL && pRoot2!=NULL)
{
if(pRoot1->val==pRoot2->val) //判断根节点,相等就继续判断其他节点是否相等
flag=Match(pRoot1,pRoot2);
if(!flag) //否则判断左儿子节点是否与子结构根节点相等
flag=HasSubtree(pRoot1->left,pRoot2);
if(!flag) //还不等,则判断右儿子节点是否与子结构根节点相等
flag=HasSubtree(pRoot1->right,pRoot2);
}
return flag;
}
bool Match(TreeNode* root1,TreeNode* root2){
if(root1 == NULL && root2 != NULL) return false;
if(root2 == NULL) return true;
if(root1->val != root2->val) return false;
//if(root1->val == root2->val)
return Match(root1->left, root2->left)&&Match(root1->right, root2->right);
}
};
答:以二叉树为例(就是二叉树的先根遍历),其他树或图的DFS在此基础上进行改进。实现代码如下:
①递归方式十分简单:
void preorder(TreeNode root){
if(root){
cout<data<<' ';
preorder(root->lchild);
preorder(root->rchild);
}
}
②非递归方式:需要使用栈作为辅助,两个循环实现;
while(t || s.empty!=True){
while(t){ //只要结点不为空就应该入栈保存,与其左右结点无关
cout<data<<' ';
push(&s,t);
t= t->lchild;
}
t=pop(&s);
t=t->rchild;
}
答:以二叉树为例(就是二叉树按层遍历),其他树或图的BFS在此基础上进行改进。使用队列queue实现,每当从队列头部弹出一个节点,就将该节点的子节点按先左后右的方式压入队尾;实现代码如下:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector PrintFromTopToBottom(TreeNode* root) {
vector v;
queue q;
if(root==NULL)
return v;
q.push(root);
while(q.size()>0){
int data=q.front()->val;
v.push_back(data);
if(q.front()->left!=NULL)
q.push(q.front()->left);
if(q.front()->right!=NULL)
q.push(q.front()->right);
q.pop();
}
return v;
}
};
int cmp(int g1, int g2, int records[][2], int n)
{
// write code here
vector max, min;
//先直接根据含有g1的比较组,将大于g1的值添加到数组max,小于g1的值添加到数组min
for (int i = 0; i < n; i++)
{
if (records[i][0] == g1)
{
min.push_back(records[i][1]);
}
if (records[i][1] == g1)
{
max.push_back(records[i][0]);
}
}
//然后根据重量大小关系的传递性,循环比较,向max中添加比max已有元素还大的值,向min中
//添加比min已有元素还小的值
int count = 0;
while (count < n) //为什么要比较n轮:最坏的情况是每轮只有比较序列中最后一对某元素被添加,故
{ //需要n轮才可以保证添加完整性
count++;
for (int i = 0; i < n; i++)
{
if (records[i][0] != g1 && records[i][1] != g1)
{
if (find(min.begin(),min.end(),records[i][0])!=min.end()) //原有min数组中发现当前数值对较大者
min.push_back(records[i][1]);
if (find(max.begin(),max.end(),records[i][1])!=max.end()) //原有max数组中发现当前数值对较小者
max.push_back(records[i][0]);
}
}
}
if (find(max.begin(),max.end(),g2)!=max.end() && find(min.begin(),min.end(),g2)==min.end())
return -1;
else if (find(max.begin(),max.end(),g2)==max.end() && find(min.begin(),min.end(),g2)!=min.end())
return 1;
else
return 0;
}
#include
#include
using namespace std;
int main(){
int source,target=0;
string str;
while(cin>>source>>target>>str){
int DecNum=0;
//区分正负数
int i=0;
if(str[0]=='-')
i=1;
else
i=0;
//转化为10进制
while(i='a' && str[i]<='z')
num=str[i]-'a'+10;
else if(str[i]>='A' && str[i]<='Z')
num=str[i]-'A'+36;
DecNum+=num;
i++;
}
//10进制转化为目标进制
string tStr;
while(DecNum>0){
string temStr;
int num=DecNum%target;
if(num<=9)
temStr=std::to_string(static_cast(num)); //VS2010未实现int转化为string
else if(num>=10 && num<=35)
temStr='a'+num-10;
else if(num>=36 && num<=61)
temStr='A'+num-36;
tStr=temStr+tStr;
DecNum=DecNum/target;
}
if(str[0]=='-')
tStr="-"+tStr;
cout<
ListNode* ReverseList(ListNode* pHead) {
ListNode *p,*q,*r;
if(pHead==NULL || pHead->next==NULL){
return pHead;
}else{
p=pHead;
q=p->next;
pHead->next=NULL;
while(q!=NULL){
r=q->next;
q->next=p;
p=q;
q=r;
}
return p;
}
}
int Count1(unsigned int v)
{
int num = 0;
while(v)
{
if(v % 2 == 1)
{
num++;
}
v = v/2;
}
return num;
}
int Count2(unsigned int v)
{
unsigned int num = 0;
while(v)
{
num += v & 0x01;
v >>= 1;
}
return num;
}
int Count3(unsigned int v)
{
int num = 0;
while(v)
{
v &= (v-1);
num++;
}
return num;
}
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL){
}
};
复杂链表复制就是指复制一个一模一样的链表,不是浅拷贝而是深拷贝。因为每个节点多了一个指向任意位置的特殊指针,常见的链表深拷贝方式(按节点对应关系一个个创建)已经不好用了,要实现复制复杂链表,最好的办法是“将新创建的节点插入到原链表对应节点的后面”,具体如下:
链接:https://www.nowcoder.com/questionTerminal/f836b2c43afc4b35ad6adc41ec941dba
来源:牛客网
RandomListNode* Clone(RandomListNode* pHead)
{
if(!pHead) return NULL;
RandomListNode *currNode = pHead;
while(currNode){
RandomListNode *node = new RandomListNode(currNode->label);
node->next = currNode->next;
currNode->next = node;
currNode = node->next;
}
currNode = pHead;
while(currNode){
RandomListNode *node = currNode->next;
if(currNode->random){
node->random = currNode->random->next;
}
currNode = node->next;
}
//拆分
RandomListNode *pCloneHead = pHead->next;
RandomListNode *tmp;
currNode = pHead;
while(currNode->next){
tmp = currNode->next;
currNode->next =tmp->next;
currNode = tmp;
}
return pCloneHead;
}
while(i=b[j]){
v.push_back(a[i]);
i++;
}
else{
v.push_back(b[j]);
j++;
}
}
if(i
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1==NULL){
return pHead2;
}else if(pHead2==NULL){
return pHead1;
}
ListNode* pHead;
vector v;
int len=0;
while(pHead1!=NULL && pHead2!=NULL){
if(pHead1->val<=pHead2->val){
v.push_back(pHead1);
pHead1=pHead1->next;
}else{
v.push_back(pHead2);
pHead2=pHead2->next;
}
len=v.size();
if(len>=2){
v[len-2]->next=v[len-1];
}
}
if(pHead1!=NULL){
v[len-1]->next=pHead1;
}else if(pHead2!=NULL)
v[len-1]->next=pHead2;
pHead=v[0];
return pHead;
}
};
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1==NULL)
return pHead2;
else if(pHead2==NULL)
return pHead1;
if(pHead1->val <= pHead2->val){
pHead1->next=Merge(pHead1->next,pHead2);
return pHead1;
}else{
pHead2->next=Merge(pHead1,pHead2->next);
return pHead2;
}
}
vector Permutation(string str){
vector v;
if(str=="")
return v;
sort(str.begin(), str.end());//必须先递增排序,使得str是最小一种排序
do
{
v.push_back(str);
}
while (next_permutation(str.begin(), str.end()));//每一次循环返回true表示找到了比str大的字符串
return v;
}
bool cmp(const char a,const char b){
return a>b;
}
vector Permutation(string str){
vector v;
if(str=="")
return v;
sort(str.begin(), str.end(),cmp);//此处不同
do
{
v.push_back(str);
}
while (prev_permutation(str.begin(), str.end()));
return v;
}
vector permutation(string str)
{
vector v;
if(str.empty())
return v;
int length=str.size();
int fromIndex, changeIndex;
sort(str.begin(), str.end()); //先升序排列,获取最小的字符串组合
do
{
//保存当前获得的一种全排列组合
v.push_back(str);
fromIndex = length - 1;
//(1)向前查找第一个由大变小的元素位置
while (fromIndex > 0 && str[fromIndex] <= str[fromIndex - 1])
--fromIndex;
changeIndex = fromIndex;
if (fromIndex == 0)
break;
//(2)向后查找最后一个大于words[fromIndex-1]的元素
while (changeIndex + 1< length && str[changeIndex + 1] >= str[fromIndex - 1])
++changeIndex;
//(3)交换两个值
swap(str[fromIndex - 1], str[changeIndex]);
//(4)对后面的所有值进行反向处理
reverse(str.begin()+fromIndex, str.end());
}
while (true);
return v;
}
void PermutationHelp(vector &ans, int k, string str) //遍历第k位的所有可能
{
if(k == str.size() - 1)
ans.push_back(str);
for(int i = k; i < str.size(); i++)
{
if(i != k && str[k] == str[i])
continue;
swap(str[i], str[k]);
PermutationHelp(ans, k + 1, str);
}
}
vector Permutation(string str) {
sort(str.begin(), str.end());
vector ans;
PermutationHelp(ans, 0, str);
return ans;
}
答:思想:创建3个指针,分别指向上一个节点、当前节点、下一个节点,遍历整个链表的同时,将正在访问的节点指向上一个节点,当遍历结束后,就同时完成了链表的反转。
实现代码:
ListNode* ReverseList(ListNode* pHead) {
ListNode *p,*q,*r;
if(pHead==NULL || pHead->next==NULL){
return pHead;
}else{
p=pHead;
q=p->next;
pHead->next=NULL;
while(q!=NULL){
r=q->next;
q->next=p;
p=q;
q=r;
}
return p;
}
}
int popBottom(Stack stack){
int result = stack.pop();
if(stack.isEmpty()){//弹出一个栈顶元素后,栈为空了,表示该元素就是栈底元素
return result;
}else{
int last = popBottom(stack);
stack.push(result);//注意!!!这里是把前面拿到的元素压入,这样栈底元素才不会再次压入到栈中
return last;
}
}
(6)栈中元素
排序(最多使用一个辅助栈):假设栈stack是存放原来数据的,再定义一个辅助栈help,先从stack栈中取出栈顶元素pop,将pop和help中栈顶元素比较,如果pop <= help栈顶元素,将pop压入到help栈中;如果pop > help栈顶元素,取出help栈顶元素,将其放入到stack栈中,直到help为空或者pop <= help栈顶元素。
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
TreeNode* Tree2List(TreeNode* pRootOfTree){
TreeNode *Node;
if(pRootOfTree->left==NULL && pRootOfTree->right==NULL)//叶子就返回,否则继续下面的递归
return pRootOfTree;
//思想:无论左右子树,都返回子树中最大的节点(左子树最大节点是左子树根,右子树最大节点是右子树最靠右的节点)
if(pRootOfTree->left!=NULL)
{
Node=Tree2List(pRootOfTree->left);
pRootOfTree->left=Node; //将当前节点链接在左子树最大节点右边
Node->right=pRootOfTree;
Node=pRootOfTree; //(1)返回当前树的最大节点(当前树的根节点)
}
if(pRootOfTree->right!=NULL)
{
Node=Tree2List(pRootOfTree->right);//(2)子树返回值(最大节点)
TreeNode *temNode=Node;
while(temNode->left!=NULL)//获取右子树最小节点
temNode=temNode->left;
pRootOfTree->right=temNode;//将当前节点链接在右子树最小节点的左边
temNode->left=pRootOfTree;
}
return Node; //最后返回当前树的最大节点(无右子树就返回根节点(1),有右子树就返回右子树最大节点(2))
}
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree==NULL)
return NULL;
TreeNode* node=Tree2List(pRootOfTree);
while(node->left!=NULL) //要求返回最左边第一个节点,所以作如下操作
node=node->left;
return node;
}
int NumberOf1Between1AndN_Solution(int n)
{
int ones = 0;
for (int m = 1; m <= n; m *= 10) {
int a = n/m, b = n%m;
ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
}
return ones;
}
static bool cmp(int a,int b){
string A="";
string B="";
A=A+to_string((long long)a);
A=A+to_string((long long)b);
B=B+to_string((long long)b);
B=B+to_string((long long)a);
return A numbers) {
sort(numbers.begin(),numbers.end(),cmp);
string resStr;
for(int k=0;k
int GetUglyNumber_Solution(int index) {
vector res(index);
res[0] = 1;
int t2 = 0, t3 = 0, t5 = 0, i;
for (i = 1; i < index; ++i)
{
res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
if (res[i] == res[t2] * 2)t2++;
if (res[i] == res[t3] * 3)t3++;
if (res[i] == res[t5] * 5)t5++;
}
return res[index - 1];
}
int mergeSort(vector& data, int start, int end) {
// 递归终止条件
if(start >= end) {
return 0;
}
// 递归
int mid = (start + end) / 2;
int leftCounts = mergeSort(data, start, mid);
int rightCounts = mergeSort(data, mid+1, end);
// 归并排序,并计算本次逆序对数
vector copy(data); // 数组副本,用于归并排序
int foreIdx = mid;// 前半部分的指标
int backIdx = end;// 后半部分的指标
int counts = 0;// 记录本次逆序对数
int idxCopy = end;// 辅助数组的下标
while(foreIdx>=start && backIdx >= mid+1) {
if(data[foreIdx] > data[backIdx]) {
copy[idxCopy--] = data[foreIdx--];
counts += backIdx - mid;
} else {
copy[idxCopy--] = data[backIdx--];
}
}
while(foreIdx >= start) {
copy[idxCopy--] = data[foreIdx--];
}
while(backIdx >= mid+1) {
copy[idxCopy--] = data[backIdx--];
}
for(int i=start; i<=end; i++) {
data[i] = copy[i];
}
return (leftCounts+rightCounts+counts);
}
vector maxInWindows(const vector& a, int k){
vector res;
deque d;
for(int i = 0; i < a.size(); ++i){
while(d.size()>0 && a[d.back()] <= a[i])
d.pop_back();
if(d.size()>0 && i - d.front() + 1 > k)
d.pop_front();
d.push_back(i);
if(k>0 && i+1 >= k)
res.push_back(a[d.front()]);
}
return res;
}
遍历过程中就两个动作:
priority_queue qmax;
priority_queue,greater> qmin;
void Insert(int num){
if(qmin.size()>0 && num>qmin.top())
qmin.push(num);
else
qmax.push(num);
if(qmax.size()>= qmin.size()+2){
qmin.push(qmax.top());
qmax.pop();
}else if(qmin.size()>= qmax.size()+2){
qmax.push(qmin.top());
qmin.pop();
}
}
double GetMedian(){
if(qmax.size()==qmin.size())
return (qmax.top()+qmin.top())/2.0;
else
return qmax.size()>qmin.size()? qmax.top():qmin.top();
}
double Power(double base, int exponent) {
if(exponent==0)
return 1;
if(base==0)
return 0;
double result=1;
if(exponent>0){
result=Power(base*base,exponent/2);//每一次递归调用函数,base的值就变为上一层base的2次方
if(exponent%2!=0)
result=result*base;
}
else if(exponent<0){//考虑正负次幂,将负数次幂变为正数次幂计算
base=1/base;
exponent=-1*exponent;
result=Power(base,exponent);
}
return result;
}
假设exponent=33,这样每次递归调用函数的结果如下:
unsigned long long GetBigMod(int x,int n,int mod){
if(n==0)
return 1;
int p=mod;
unsigned long long tmp=GetBigMod(x*x%p,n/2,p);
if(n%2!=0)
tmp=tmp*x%p;
return tmp;
}
int GetJiami(int x,int n,int m){
int p1=10,p2=1000000007;
return GetBigMod(GetBigMod(x,n,p1),m,p2);
}