现在的位置: 首页 > 综合 > 正文

【九度-剑指Offer题目笔记】下

2017年11月14日 ⁄ 综合 ⁄ 共 21097字 ⁄ 字号 评论关闭

题目1524:复杂链表的复制

思路:在每个链表结点后面复制此结点,将复制后的链表分离成两个链表即可,代码如下:

#include <cstdio>
//#include <iostream.h>
using namespace std;
 
struct Node {
    int val;
    Node *next;
    Node *other;
    void setValue(int val){
        this->val = val;
        next=NULL;
        other=NULL;
    }
};
 
void copyList(Node *head){
    Node *pHead = head;
    Node *nodeToCopy=head;
    //复制
    while (pHead!=NULL) {
        nodeToCopy=new Node();
        nodeToCopy->setValue(pHead->val);
        nodeToCopy->next=pHead->next;
        nodeToCopy->other=pHead->other;
        pHead->next=nodeToCopy;
        pHead=nodeToCopy->next;
    }
    //修正任意指针
    while (pHead!=NULL) {
        pHead->next->other=pHead->other->next;
        pHead=pHead->next->next;
    }
     
}
 
Node * spliteList(Node *head){
    Node *pCopy = head;
    Node *pHead = head->next;
    Node *node= head;
    while (node!=NULL) {
        pCopy=node->next;
        node->next=pCopy->next;
        node=node->next;
    }
    return pHead;
}
 
int main()
{
    int n,val;
    Node linkList[1000];
    while (scanf("%d",&n)!=EOF) {
        scanf("%d",&val);
        Node *head = new Node();
        head->setValue(val);
        Node *node=head;
        linkList[0]=*head;
        for (int i=1; i<n; i++) {
            scanf("%d",&val);
            Node *tmpNode = new Node();
            tmpNode->setValue(val);
            node->next=tmpNode;
            node=tmpNode;
            linkList[i]=*tmpNode;
        }
        node=head;
        while(node!=NULL) {
            scanf("%d",&val);
            if (val!=0) {
                node->other=&linkList[val-1];
            }
            node=node->next;
        }
        copyList(head);
        node = spliteList(head);
         
        if (node!=NULL) {
            printf("%d",node->val);
            if (node->other!=NULL) {
                printf(" %d\n",node->other->val);
            }else{
                printf(" 0\n");
            }
            node=node->next;
        }
        while (node!=NULL) {
            printf("%d",node->val);
            if (node->other!=NULL) {
                printf(" %d\n",node->other->val);
            }else{
                printf(" 0\n");
            }
            node=node->next;
        }
    }
    return  0;
}

题目1503:二叉搜索树与双向链表

思路:中序遍历,将左孩子指向前一个结点,右孩子指向下一个结点,代码如下:

#include <cstdio>
 
struct BinaryTreeNode 
{
    int   m_nValue; 
    BinaryTreeNode*        m_pLeft;  
    BinaryTreeNode*        m_pRight; 
    void setValue(int val){
        this->m_nValue=val;
        m_pLeft=m_pRight=NULL;
    }
};
 
BinaryTreeNode * constructTree(BinaryTreeNode *root){
    int val;
    scanf("%d",&val);
    if (val!=0)
    {
        root=new BinaryTreeNode();
        root->setValue(val);//同时把左右子树变为空了
        root->m_pLeft=constructTree(root->m_pLeft);
        root->m_pRight=constructTree(root->m_pRight);
    }
    return root;
}
 
void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)
{
    if(pNode == NULL)
        return;
 
    BinaryTreeNode *pCurrent = pNode;
 
    if (pCurrent->m_pLeft != NULL)
        ConvertNode(pCurrent->m_pLeft, pLastNodeInList);
 
    pCurrent->m_pLeft = *pLastNodeInList; 
 
    if(*pLastNodeInList != NULL)
        (*pLastNodeInList)->m_pRight = pCurrent;
 
    *pLastNodeInList = pCurrent;
 
    if (pCurrent->m_pRight != NULL)
        ConvertNode(pCurrent->m_pRight, pLastNodeInList);
}
 
BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
    BinaryTreeNode *pLastNodeInList = NULL;
    ConvertNode(pRootOfTree, &pLastNodeInList);
 
    // pLastNodeInList指向双向链表的尾结点,
    // 我们需要返回头结点
    BinaryTreeNode *pHeadOfList = pLastNodeInList;
    while(pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)
        pHeadOfList = pHeadOfList->m_pLeft;
 
    return pHeadOfList;
}
 
void PrintList(BinaryTreeNode* pRoot)  
{  
    if(pRoot == NULL)  
        return;  
    BinaryTreeNode* pHead = pRoot;
    while(pHead != NULL)
    {
        printf("%d ",pHead->m_nValue);
        pHead = pHead->m_pRight;
    }
}
int main()
{
    int n; 
    while (scanf("%d",&n)!=EOF) {
        for (int i=0;i<n;i++)
        {
            BinaryTreeNode *root =NULL;
            root = constructTree(root); 
            root = Convert(root);
            PrintList(root);
            printf("\n");
        }       
    }    
    return  0;
}

题目1369:字符串的排列

最简单的是调用系统函数:next_permutation,或者自己实现此函数,自己实现函数代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
 
//如果还有下一个排列,返回1,否则返回0
int next_permutation(char *arr, int len)
{
    int i, j, k, l;
    // Step 1, find the largest k s.t. arr[k] < arr[k+1]
    for (k = len - 2; k >= 0; k--)
        if (arr[k] < arr[k + 1])
            break;
    if (k < 0)
        return 0;
    // Step 2, find the largest l(el) s.t. arr[k] < arr[l]
    for (l = len - 1; l > k; l--)
        if (arr[k] < arr[l])
            break;
    // Step 3, swap arr[k] & arr[l]
    swap(arr[k] , arr[l]);
    // Step 4, reverse arr[k+1, ]
    reverse(arr+k+1,arr+len);
    return 1;
}
int main(){
    char str[10];
    while(gets(str)){
        int len=strlen(str);
        sort(str,str+len);
        puts(str);
        while(next_permutation(str,len)){
            puts(str);
        }
    }
    return 0;
}

题目1370:数组中出现次数超过一半的数字

有多种方法,比如先排序(全排序,或用快排只定位出中间数),找中间的数,因为超过一半的数肯定在中间。剑指offer里提供了另一种巧妙的方法:遍历数组,遇到相同的数则加1,否则减1,如果小于0,则以新数为起点,并标记为1.最后剩下的数必然是可能超过一半的数。代码如下:

#include<stdio.h>
#include<algorithm>

using namespace std;


bool CheckMoreThanHalf(int* numbers, int length, int number)
{
    int times = 0;
    for(int i = 0; i < length; ++i)
    {
        if(numbers[i] == number)
            times++;
    }
    return times  > length>>1;
}


int getMoreThanHalfNumber(int data[],int n)
{
    if(data == NULL  || n <= 0)
        return -1;
    
    int result = data[0];
    int times = 1;
    for(int i = 1; i < n; ++i)
    {
        if(times == 0)
        {
            result = data[i];
            times = 1;
        }
        else if(data[i] == result)
            times++;
        else
            times--;
    }
    
    return CheckMoreThanHalf(data, n, result)?result:-1;
}
int main()
{
    int data[100005];
    int n;
    while(scanf("%d",&n)!=EOF){
        for (int i=0;i<n;i++)
        {
            scanf("%d",&data[i]);
        }
        printf("%d\n",getMoreThanHalfNumber(data,n));
    }
    return 0;
}

题目1371:最小的K个数

跟上题一样,可以用普通排序或快排的灵活应用解决,代码如下:

普通排序:

#include<stdio.h>
#include<algorithm>
#define N 200001
using namespace std;
 
void getMinKNumber(int data[],int n,int k)
{
    if (k>n)
    {
        return ;
    }
     
    sort(data,data+n);  
    for(int i=0;i<k-1;i++)
    {
        printf("%d ",data[i]);
    }
    printf("%d\n",data[k-1]);
}
int main()
{
    int n,k;
    int data[N];
     
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&data[i]);
        }
        getMinKNumber(data,n,k);
    }
}

快排灵活应用:

#include<stdio.h>
#include<algorithm>
#define N 200001
using namespace std;
 
int Partition(int data[], int length, int start, int end)
{
    if(data == NULL || length <= 0 || start < 0 || end >= length)
        return -1;
 
    int index = start;
    swap(data[index], data[end]);
 
    int small = start - 1;
    for(index = start; index < end; ++ index)
    {       
        if(data[index] < data[end])
        {
            ++ small;
            //如果当前遍历值比取的值小且当前值不是small指向的值,把小的值往前置换
            if(small != index)
                swap(data[index], data[small]);
        }
    }
 
    ++ small;
    swap(data[small], data[end]);
 
    return small;
}
 
//利用快排找出前k个数,再排序
void getMinKNumber(int data[],int n,int k)
{
    if(data == NULL || k > n || n <= 0 || k <= 0)
        return;
 
    int start = 0;
    int end = n - 1;
    int index = Partition(data, n, start, end);
    while(index != k - 1&&index!=-1)
    {
        if(index > k - 1)
        {
            end = index - 1;
            index = Partition(data, n, start, end);
        }
        else
        {
            start = index + 1;
            index = Partition(data, n, start, end);
        }
    }
 
    sort(data,data+k);
 
    for(int i=0;i<k-1;i++)
    {
        printf("%d ",data[i]);
    }
    printf("%d\n",data[k-1]);
}
int main()
{
    int n,k;
    int data[N];
 
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&data[i]);
        }
        getMinKNumber(data,n,k);
    }
}

题目1372:最大子向量和(连续子数组的最大和)

记录当前和以及最大和,如果当前和大于最大和,则更新最大和,如果当前和小于0,则重新计当前和,代码如下:

#include <cstdio>
//#include <iostream.h>
using namespace std;
 
int main()
{
    int n,maxSum,maxStartIndex,currentStartIndex,eIndex,currentSum;
    int data;
    while (scanf("%d",&n)&&n!=0) {
        maxSum = -1001;
        currentSum = 0;
        maxStartIndex = 0;
        eIndex=0;
        currentStartIndex = 0;
 
        for (int i=0; i<n; i++) {
            scanf("%d",&data);
            currentSum+=data;
            //如果当前和大于最大和,则更新最大和,并更新位置
            if (currentSum>maxSum) {
                eIndex=i;
                maxStartIndex=currentStartIndex;
                maxSum = currentSum;
            }
            //如果当前和小于0,则重新开始计和,并更新起始位置
            if(currentSum<0){
                currentSum = 0;
                currentStartIndex = i+1;
            }            
        }
        printf("%d %d %d\n",maxSum,maxStartIndex,eIndex);
    }    
    return  0;
}

题目1373:整数中1出现的次数(从1到n整数中1出现的次数)

将数据分段来求,利用数学规律,代码如下:

普通方法:

#include <cstdio>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
 
 
int NumberOf1(const char* strN)
{
    if(!strN || *strN < '0' || *strN > '9' || *strN == '\0')
        return 0;
     
     
    int first = *strN - '0';
    unsigned int length = static_cast<unsigned int>(strlen(strN));
     
     
    if(length == 1 && first == 0)
        return 0;
     
     
    if(length == 1 && first > 0)
        return 1;
     
     
    // 假设strN是"21345"
    // numFirstDigit是数字10000-19999的第一个位中1的数目
    int numFirstDigit = 0;
    if(first > 1)
        numFirstDigit = pow(10,length - 1);
    else if(first == 1)
        numFirstDigit = atoi(strN + 1) + 1;
     
     
    // numOtherDigits是01346-21345除了第一位之外的数位中1的数目
    int numOtherDigits = first * (length - 1) * pow(10,length - 2);
    // numRecursive是1-1345中1的数目
    int numRecursive = NumberOf1(strN + 1);
     
     
    return numFirstDigit + numOtherDigits + numRecursive;
}
 
 
 
 
int main()
{
    int a,b;
    char c1[15],c2[15];
    while (scanf("%d%d",&a,&b)!=EOF) {
        if (a>b)
        {
            swap(a,b);
        }
         
        int n=0;
        if (a>0) {
            sprintf(c1, "%d",a-1);
            n=NumberOf1(c1);
        }
         
        int m=0;
        if (b>0) {
            sprintf(c2, "%d",b);
            m=NumberOf1(c2);
        }
         
        printf("%d\n",m-n);
    }    
    return  0;
}

高手的代码:

#include <stdio.h>
int f(int z) 
{
    int i=9,c=0,v=1e9,d;
    while(z){
        for (;v>z;v/=10,--i);
        d=z/v;//当前最高位数字
        z%=v;//除去最高位剩下的数字
        //最高位大于1,则最高位为1的情况有v种,否则为除去最高位剩下的数字加上1种
        //除去最高位为1的情况,将剩下的数字按整(v/10)划分,可划分为d份,第一份中任选一位为1,其他位可以0~9之间任选,根据排列组合
        //有d*v/10*i种情况。1的总的个数为两者两加
        c+=d*v/10*i+(d>1?v:z+1);
    }
    return c;
}
 
int main()
{
    int a,b,i;
    while(scanf("%d %d",&a,&b)!=EOF) 
    {
        //a>b时用异或的方法调换一下,
        //原理,如果a与b中某一位相同,则异或之后为0,如果此位为1(0),则0与了b中的1(0)异或之后依然是1(0)
        //如果a与b中某一位不相同,则异或之后为1,如果a的此位为1(0),则1与b中的0(1)异或之后是1(0)
        //两种情况均实现了此位上的a与b的调换
        a>b?(a^=b,b^=a,a^=b):0;
        printf("%d\n",f(b)-(a?f(a-1):0));
    }
}

题目1504:把数组排成最小的数

关键在于定义比较函数,比较的是两个数拼接后的字符串大小,而不是两个数的大小,代码如下:

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
#define MAXN 101
using namespace std;
string str[MAXN];
int cmp(string a,string b)      
{
    string str1=a+b;
    string str2=b+a;
    if(str1<str2) return 1;
    else return 0;
}
int main()
{
    int n;
    int i,j;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) continue;
        for(i=0;i<n;i++) cin>>str[i];
        sort(str,str+n,cmp);
        for(i=0;i<n;i++) cout<<str[i];
        cout<<endl;
    }
    return 0;
}

题目1214:丑数

通过前面的丑数产生后面的丑数,代码如下:

#include <cstdio>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
 
int getMin(int i,int j,int k){
    return i<j?(i<k?i:k):(j<k?j:k);
}
 
int numbers[1500];
int getUglyNumber(int n){
    numbers[0]=1;
    int *number2 = numbers;
    int *number3 = numbers;
    int *number5 = numbers;
    int index=1;
    while (index<n) {
        numbers[index]=getMin(*number2*2, *number3*3, *number5*5);
         
        while (*number2*2<=numbers[index]) {
            number2++;
        }
        while (*number3*3<=numbers[index]) {
            number3++;
        }
        while (*number5*5<=numbers[index]) {
            number5++;
        }
        index++;
    }
    return numbers[index-1];
}
 
int main()
{
    int n;
    while (scanf("%d",&n)!=EOF) {
         
        printf("%d\n",getUglyNumber(n));
    }    
    return  0;
}

题目1283:第一个只出现一次的字符

遍历两次,第一次统计每个字符出现的次数(用一个长度为256的数组存储),第二次找到只出现一次的字符,代码如下:

#include <cstdio>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
#define MAPLENGTH 256
 
 
char getFirstChar(char *str){
    int map[MAPLENGTH];
    unsigned long len=strlen(str);
    int end='A'+26;
    for (int i='A'; i<end; i++) {
        map[i]=0;
    }
    for (int i=0; i<len; i++) {
        map[str[i]]++;
    }
    for (int i=0; i<len; i++) {
        if(map[str[i]]==1){
            return i;
        }
    }
    return -1;
}
 
int main()
{
    char str[10001];
    while (scanf("%s",str)!=EOF) {
        printf("%d\n",getFirstChar(str));
    }    
    return  0;
}

题目1348:数组中的逆序对

归并排序的同时统计逆序对,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 100001
using namespace std;
 
int copys[LENGTH];
 
 
long long InversePairs(int nums[],int copys[],int s,int e)
{
    if (s==e) {
        copys[s]=nums[s];
        return 0;
    }
     
    int length = (e-s)>>1;
     
    long long left =InversePairs(copys, nums, s, s+length);
    long long right=InversePairs(copys, nums, s+length+1, e);
     
    long long count=0;
    int rightStart = s+length;
    int i=s+length;
    int copyIndex=e;
    while (i>=s&&e>rightStart) {
        if (nums[i]>nums[e]) {
            copys[copyIndex--]=nums[i--];
            count+=e-rightStart;
        }else{
            copys[copyIndex--]=nums[e--];
        }
    }
    for (; i>=s; i--) {
        copys[copyIndex--]=nums[i];
    }
    for (; e>rightStart; e--) {
        copys[copyIndex--]=nums[e];
    }
    return left+right+count;
}
 
long long InversePairs(int nums[],int length)
{
    if (nums==NULL||length<=0) {
        return 0;
    }
    for (int i=0; i<length; i++) {
        copys[i]=nums[i];
    }
    return InversePairs(nums,copys,0,length-1);
}
 
int main()
{
    int n;
    int nums[LENGTH];
    while(scanf("%d",&n)!=EOF)
    {
         
        for (int i=0; i<n; i++) {
            scanf("%d",&nums[i]);
        }
        printf("%lld\n",InversePairs(nums,n));
    }
    return 0;
}

题目1505:两个链表的第一个公共结点

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 1001
using namespace std;
 
/*
 *这道题推荐的解法是首先遍历链表长度,长的链表先走两个长度之差步,然后同时往前走,第一个相同的点就是第一个公共结点
 *由于题目给的输入不好构建这两个链表,故用了偷懒的方法了。
 */
 
void GetLastSame(int nums1[],int nums2[],int m,int n)
{
    int i=0;
    int *shortNum = nums1;
    int *longNum = nums2;
    int k,min;
    if (m>n) {
        shortNum=nums2;
        longNum=nums1;
        k =m-n;
        min = n;
    }else{
        k =n - m;
        min = m;
    }
    while (longNum[k+i]!=shortNum[i]&&i<min) {
        i++;
    }
     
    if (i>=min) {
        printf("My God\n");
    }else{
        printf("%d\n",shortNum[i]);
    }
}
 
 
int main()
{
    int m,n;
    int nums1[LENGTH];
    int nums2[LENGTH];
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        for (int i=0; i<m; i++) {
            scanf("%d",&nums1[i]);
        }
        for (int i=0; i<n; i++) {
            scanf("%d",&nums2[i]);
        }
        GetLastSame(nums1,nums2,m,n);
         
    }
    return 0;
}

题目1349:数字在排序数组中出现的次数

用二分法找到该数第一次出现和最后一次出现的位置,相减得出,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 1000001
using namespace std;
 
int GetFirstK(int *nums,int length,int k,int s,int e)
{
    if (s>e) {
        return -1;
    }
    int mid = (s+e)>>1;
    int midData =nums[mid];
    if (midData==k) {
        if (mid==0||nums[mid-1]!=k) {
            return mid;
        }else{
            e=mid - 1;
        }
    }else if(k<midData){
        e=mid-1;
    }else{
        s=mid + 1;
    }
     
    return GetFirstK(nums, length, k,s, e);
}
 
int GetLastK(int *nums,int length,int k,int s,int e)
{
    if (s>e) {
        return -1;
    }
    int mid = (s+e)>>1;
    int midData =nums[mid];
    if (midData==k) {
        if (mid==length-1||nums[mid+1]!=k) {
            return mid;
        }else{
            s=mid + 1;
        }
    }else if(k<midData){
        e=mid-1;
    }else{
        s=mid + 1;
    }
     
    return GetLastK(nums, length, k,s, e);
 
}
 
int getKCounts(int *nums,int length,int k){
    int count = 0;
    if (nums!=NULL&&length>0) {
        int first = GetFirstK(nums,length,k, 0, length-1);
        int last = GetLastK(nums,length,k, 0, length-1);
        if (first!=-1&&last!=-1) {
            count = last - first +1;
        }
    }
    return count;
}
 
int main()
{
    int m,n,k;
    int nums[LENGTH];
    while(scanf("%d",&m)!=EOF)
    {
        for (int i=0; i<m; i++) {
            scanf("%d",&nums[i]);
        }
        scanf("%d",&n);
        for (int i=0; i<n; i++) {
            scanf("%d",&k);
            printf("%d\n",getKCounts(nums, m,k));
        }
    }
    return 0;
}

题目1350:二叉树的深度

先求左右子树的深度,递归求解,代码如下:

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
#define MAXN 11
using namespace std;
 
typedef struct {  
    //int val;  
    int left;  
    int right;  
} BinaryTreeNode; 
 
int getTreeDepth(BinaryTreeNode *notes,int idx){
    if (idx==-2) return 0;
    int left = notes[idx].left==-2?0:getTreeDepth(notes,notes[idx].left);
    int right = notes[idx].right==-2?0:getTreeDepth(notes,notes[idx].right);
    return left>right?left+1:right+1;
}
 
int main()
{
    int n;
    BinaryTreeNode notes[MAXN];
    while(cin >>n)
    {
        if(n==0) continue;
        for(int i=0;i<n;i++)
        {
            cin >> notes[i].left >> notes[i].right;  
            --notes[i].left;  
            --notes[i].right;
        }
         
        int depth = getTreeDepth(notes,0);
        cout<<depth<<endl;
    }
    return 0;
}

题目1351:数组中只出现一次的数字

先按位异或,再通过异或结果对数组分组(根据某位是否为1),再分别异或,代码如下:

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
#define MAXN 1000000
using namespace std;
 
int nums[MAXN];
 
int getNumber(int *num,int *num1,int *num2 , int n){
    int resultExclusiveOR = 0;
    int  mask = 1;
    for (int i = 0; i < n; i++)
        resultExclusiveOR^=num[i];
    *num1=0;
    *num2=0;
    while( !(mask & resultExclusiveOR) )  mask <<= 1;
    for (int i = 0; i < n; i++)
    {
        if (num[i]& mask)
            *num1^=num[i];
        else
            *num2^=num[i];
    }
    if(*num1>*num2)
    {
        //交换
        *num1^=*num2;
        *num2^=*num1;
        *num1^=*num2;
    }
    return 0;
}
 
template <class T>  
inline void scan_d(T &ret) {  
    char c; ret=0;  
    while((c=getchar())<'0'||c>'9');  
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();  
}
int main()
{
    int n;
     
    while(cin >>n)
    {
        for(int i=0;i<n;i++)
        {
            scan_d(nums[i]);
        }
        if(n<2) continue;
        int num1,num2;
        getNumber(nums,&num1,&num2,n);
        cout<<num1<<" "<<num2<<endl;
    }
    return 0;
}

mask也可用(resultExclusiveOR & (-resultExclusiveOR))求得,scan_d可以大提高输入速度。

题目1352:和为S的两个数字

分别定义两个指针从前往后或从后往前移动,大小S则移动后面的指针,小于则移动前面的指针,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 1000001
using namespace std;
int nums[LENGTH];
 
void findSum(int &num1,int &num2,int n,int k){
    num1=num2=-1;
    int s=0,e=n-1;
    while ( s<e ) {
        if (nums[s]+nums[e]<k) {
            s++;
        }else if (nums[s]+nums[e]>k) {
            e--;
        }else{
            num1 = nums[s];
            num2 = nums[e];
            break;
        }
    }
}
 
int main()
{
    int n,k;
     
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for (int i=0; i<n; i++) {
            scanf("%d",&nums[i]);
        }
        int num1,num2;
        findSum(num1,num2,n,k);
        printf("%d %d\n",num1,num2);
    }
    return 0;
}

题目1354:和为S的连续正数序列

类似于上面一题,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 1000001
using namespace std;
 
 
void printSum(int s,int e){
    while (s<e) {
        printf("%d ",s++);
    }
    printf("%d\n",e);
}
 
void findSum(int n){
    int mid = (n>>1)+1;
    int s=1,e=2;
    int sum = s+e;
    bool find = false;
    while (s<mid&&s<e) {
        if (sum<n) {
            e++;
            sum+=e;
        }else if (sum>n){
            sum-=s;
            s++;
        }else{
            find = true;
            printSum(s,e);
            sum-=s;
            s++;
            e++;
            sum+=e;
        }
    }
    if (!find) {
        printf("Pity!\n");
    }
}
 
 
 
int main()
{
    int n;
     
    while(scanf("%d",&n)!=EOF&&n>-1)
    {
        findSum(n);
        printf("#\n");
    }
    return 0;
}

题目1361:翻转单词顺序

先全部翻转,再逐个翻转,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 50001
using namespace std;
 
char strs[LENGTH];
 
void reverseChar(char *pBegin,char *pEnd){
    if (pBegin==NULL||pEnd==NULL) {
        return;
    }
    while (pBegin<pEnd) {
        swap(*pBegin, *pEnd);
        pBegin++;
        pEnd--;
    }
}
 
void reverseChar(char *str){
    char *pBegin = str;
    char *pEnd = str;
    while (*pEnd!='\0') pEnd++;
    pEnd--;
     
    reverseChar(pBegin, pEnd);
     
    pEnd=pBegin;
    while (*pBegin!='\0') {
        if (*pEnd==' '||*pEnd=='\0') {
            if (*pBegin!=' ') {
                reverseChar(pBegin, pEnd-1);
                pBegin = pEnd+1;
                pEnd++;
            }else{
                pBegin = pEnd+1;
                pEnd++;
            }
        }else{
            pEnd++;
        }
    }
     
    printf("%s\n",str);
}
 
 
 
int main()
{
    while(gets(strs))
    {
        reverseChar(strs);
    }
    return 0;
}

题目1362:左旋转字符串

先翻转前后两段,再全部翻转,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define LENGTH 1001
using namespace std;
 
char strs[LENGTH];
 
void reverseChar(char *pBegin,char *pEnd){
    if (pBegin==NULL||pEnd==NULL) {
        return;
    }
    while (pBegin<pEnd) {
        swap(*pBegin, *pEnd);
        pBegin++;
        pEnd--;
    }
}
 
void reverseChar(char *str,int k){
    long length = strlen(str);
     
    k = k%length;
    //翻转前半段
    reverseChar(str,str+k-1);
     
    //翻转后半段
    reverseChar(str+k,str+length-1);
     
    //翻转全部
    reverseChar(str,str+length-1);
     
    printf("%s\n",str);
}
 
 
 
int main()
{
    int k;
    while(scanf("%s%d",strs,&k)!=EOF)
    {
        reverseChar(strs,k);
    }
    return 0;
}

题目1360:乐透之猜数游戏

通过f(n)=f(n-6)+f(n-5)..f(1)求得(假设最大点数为6),用两个数组交替存储。注意要先取两位小数后再比较,不然a不了,代码如下:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <iostream>
int m=0;
int n=0;
int flag =0;
double a[2][8*10+1];//用两行表示数据,每行都是n个骰子相加能得到的和
 
 
void count(int n,int m)
{
    flag=1;
    memset(a,0,sizeof(a));
    for(int i=1;i<=m;i++)//第一个骰子
    {
        a[1][i]=1.0/m;
    }
    for(int i=2;i<=n;i++)
    {
        for(int l=0;l<i;l++) 
            a[1-flag][l]=0;
         
        for(int j=0;j<=m*i;j++)//计算下一行对应位置的值,依次遍历其前一行每个和的概率,再乘以这个骰子得到对应值的概率,最后相加。
            //即考虑了所有这个值能够构成的情况
        {
            a[1-flag][j] = 0;
            int k = 1;
            while(k<=j && k<=m)
            {
                a[1-flag][j] += a[flag][j-k]/m;
                k++;
            }
        }
        flag = 1- flag;
    }
     
}
 
 
void MaxNum(double *data, int len, double &max, int &index)
{
    max = data[0];
    for (int i = 1; i < len; ++i)
    {
        if (max < data[i])
        {
            max = data[i];
            index = i;
        }
    }
    data[index] = 0;
}
 
 
int main()
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        if(n==0)
            break;
         
         
         
        count(n,m);
        for (int i = n; i <= n * m; ++i)
        {
            a[flag][i] = (int) (a[flag][i] * 100 + 0.5) / 100.0;
        }
         
        double max=0;
        int maxi=0;
         
        for(int i=0;i<3;i++)
        {
            MaxNum(a[flag],n*m+1,max,maxi);
            printf("%d %.2f\n",maxi, max);
        }
         
        printf("\n");
         
    }
     
}

题目1355:扑克牌顺子

先排序,再用0补全空档,代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[20];
int main()
{
    int n, num;
    int i;
    while(scanf("%d", &n) && n)
    {
        for(i=0; i<n; i++)
            scanf("%d", &a[i]);
        sort(a, a+n); //小到大排序
        num = 0;
        i = 0;
        while(i<n && a[i]==0)
        {
            num++;
            i++;
        }
        //num为大小王张数
        for(i++; i<n; i++)
        {
            if(a[i] == a[i-1])
                break;  //出现相同无法形成顺子
            else
                //用num减去两张牌的差值,即用大小王填补
                num -= (a[i]-a[i-1]-1);
        }
        //cout << num << endl;
        if(i<n || num<0)
            //i<n 出现相同牌(除大小王外)
            //num<0 需要填补的大小王牌数大于实际大小王牌数
            printf("Oh My God!\n");
        else
            printf("So Lucky!\n");
    }
    return 0;
}

题目1356:孩子们的游戏(圆圈中最后剩下的数)

用循环链表,或使用数学公式,后者代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
 
int main()
{
    int n, m;
    while(scanf("%d", &n)&&n>0)
    {
        scanf("%d", &m);
        int last=0;
        for (int i=2; i<=n; i++) {
            last=(last+m)%i;
        }
        printf("%d\n",last+1);
    }
    return 0;
}

题目1506:求1+2+3+...+n

使用构造函数+静态变量,或使用虚函数求解,代码如下:

1、

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
 
class A;
A* array[2];
 
class A{
public:
    virtual unsigned int sum(unsigned int n){
        return 0;
    }
};
 
class B:public A {
public:
    virtual unsigned int sum(unsigned int n){
        return array[!!n]->sum(n-1)+n;
    }
};
 
int main()
{
    int n=10;
    A a;
    B b;
    array[0]=&a;
    array[1]=&b;
    while (cin>>n) {
        int value = array[1]->sum(n);
        printf("%d\n",value);
    }
     
    return 0;
}

2、

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
using namespace std;
 
class A{
public:
    A(){
        ++n;
        sum+=n;
    }
    static void reset(){
        n=0;
        sum=0;
    }
    static unsigned int getSum(){
        return sum;
    }
private:
    unsigned static int n;
    unsigned static int sum;
     
};
 
unsigned int A::n=0;
unsigned int A::sum=0;
 
int main()
{
    int n;
    while (cin>>n) {
        A::reset();
        A *a=new A[n];
        int value = A::getSum();
        printf("%d\n",value);
        delete a;
        a=NULL;
    }
     
    return 0;
}

题目1507:不用加减乘除做加法

用位运算,先按位异或得到a,再按位与得到b,b<<=1,再对ab做相同操作。效果比直接相加高,代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <stdlib.h>
using namespace std;
 
int add(int m,int n){
    int sum,carry;
    do {
        sum=m^n;
        carry=(m&n)<<1;
        m=sum;
        n=carry;
         
    } while (n);
    return sum;
}
 
int main()
{
    int m,n;
    while (cin>>m>>n) {
        printf("%d\n",add(m,n));
    }
     
    return 0;
}

题目1508:把字符串转换成整数

要考虑各种情况,代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
using namespace std;
 
enum Statues{k_valid=0,k_invalid=1};
int g_invalid = k_valid;
 
long long atoiCore(const char *p,bool minus){
    long long result = 0;
    int flag=minus?-1:1;
    while (*p!='\0') {
        if (*p<='9'&&*p>='0') {
            result=result*10+flag*(*p-'0');
            if ((!minus&&result>0x7FFFFFFF)||(minus&&result<(signed int)0x80000000)) {
                result = 0;
                break;
            }
        }else{
            result = 0;
            break;
        }
        p++;
    }
    if (*p=='\0') {
        g_invalid = k_valid;
    }
     
    return result;
}
 
int atoi(const char *p){
    long long result = 0;
    g_invalid=k_invalid;
    if (p!=NULL||*p!='\0') {
         
        bool minus = false;
        if (*p=='+') {
            p++;
        }else if (*p=='-') {
            p++;
            minus = true;
        }
        if (*p!='\0') {
            result = atoiCore(p,minus);
        }
    }
     
    return (int)result;
}
 
 
 
 
 
int main()
{
    char strs[50];
    while(scanf("%s",strs)!=EOF)
    {
        int n = atoi(strs);
        if (!n&&k_invalid==g_invalid)
            printf("My God\n");
        else
            printf("%d\n",n);
    }
    return 0;
}

题目1509:树中两个结点的最低公共祖先

先找到两个结点的路径,再找公共祖先,代码如下:

#include <cstdio>
#include <vector>
 
using namespace std;
 
struct BinaryTreeNode
{
    int   m_nValue;
    BinaryTreeNode*        m_pLeft;
    BinaryTreeNode*        m_pRight;
    void setValue(int val){
        this->m_nValue=val;
        m_pLeft=m_pRight=NULL;
    }
};
 
BinaryTreeNode * constructTree(BinaryTreeNode *root){
    int val;
    scanf("%d",&val);
    if (val!=0)
    {
        root=new BinaryTreeNode();
        root->setValue(val);//同时把左右子树变为空了
        root->m_pLeft=constructTree(root->m_pLeft);
        root->m_pRight=constructTree(root->m_pRight);
    }
    return root;
}
 
bool getNodePath(BinaryTreeNode* pRoot,int pNode,vector<BinaryTreeNode*> &path){
    bool found = false;
    path.push_back(pRoot);
    if (pRoot->m_nValue==pNode) {
        return true;
    }
    if (pRoot->m_pLeft!=NULL) {
        found=getNodePath(pRoot->m_pLeft,pNode,path);
    }
    if (!found&&pRoot->m_pRight!=NULL) {
        found=getNodePath(pRoot->m_pRight,pNode,path);
    }
    if (!found) {
        path.pop_back();
    }
    return found;
}
 
void PrintTree(const vector<BinaryTreeNode*> &path1,const vector<BinaryTreeNode*> &path2)
{
    vector<BinaryTreeNode*>::const_iterator it1 = path1.begin();
    vector<BinaryTreeNode*>::const_iterator it2 = path2.begin();
    BinaryTreeNode* last=NULL;
    while (it1!=path1.end()&&it2!=path2.end()) {
        if (*it1==*it2) {
            last=*it1;
        }else{
            break;
        }
        it1++;
        it2++;
    }
     
    if (last!=NULL) {
        printf("%d\n",last->m_nValue);
    }else{
        printf("My God\n");
    }
}
void freeTree(BinaryTreeNode* pRoot){
     
}
int main()
{
    int n,m1,m2;
    while (scanf("%d",&n)!=EOF) {
        for (int i=0;i<n;i++)
        {
            BinaryTreeNode *root =NULL;
            root = constructTree(root);
            scanf("%d%d",&m1,&m2);
            if (m1==0||m2==0) {
                printf("My God\n");
                continue;
            }
            vector<BinaryTreeNode*> path1,path2;
            getNodePath(root,m1,path1);
            getNodePath(root,m2,path2);
            PrintTree(path1,path2);
            freeTree(root);
        }
    }
    return  0;
}

抱歉!评论已关闭.