algorithm: 基于C++和Python(一)


代码 Python和C++

1.两数之和

给定一个整数数组如[2,7,11,15]和一个目标值target=9,找出数组中和为目标值的两个数,返回结果索引[0,1]。
算法思想:遍历数组数组的字典保存需要的值也就是target - numbers,当后面遍历的数字存在于该字典时满足条件返回,只需遍历一次数组。

Python实现

class Solution(object):
    def twosum(self,nums,target):
        """
        :param nums: list
        :param target: int
        :return: list index
        """
        d={}
        for i,num in enumerate(nums):
            if target-num in d:
                print([d[target-num],i])
                return [d[target-num],i]  # 返回对应原列表的两个索引
            d[num] = i
b = Solution()
b.twosum([2,3,4,5,6],9)

C++版本

#include <iostream>
#include <vector>
#include<unordered_map>
// Two Sum
using namespace std;
class Solution{
public:
    vector<int> twosum(vector<int> numbers,int target){
        unordered_map<int,int> m;
        vector<int>result;
        for(int i=0;i<numbers.size();i++){
            if(m.find(numbers[i])==m.end()){ //number[i]不在m中
                m[target-numbers[i]]=i; // 将key=target-numbers[i]的索引i计入unordered map的value中
            }
            else{
                result.push_back(m[numbers[i]]+1); //第一个值的索引
                result.push_back(i+1); // 第二个值的索引
                break;
            }
        }
        return result;
    }
};
int main(){
    
    vector<int> numbers{2,3,4,5,6},out;
    int target=9;
    vector<int> (Solution::*pfunc)(vector<int> numbers,int target) = &Solution::twosum; // 调用类成员函数
    Solution test;
    out=(test.*pfunc)(numbers,target);
    for(int i=0;i<out.size();i++) cout<<out[i]<<" ";
    cout<<""<<endl;
    return 0;
}

2.两数相加

给定两个非空链表表示两个非负整数,位数按逆序方式存储,每个节点只存储单个数字,将两数相加返回一个新列表。
输入: (2->4->3) + (5->6->4)
输出:7->0->8
算法思想:一步步取值,计算sum通过取余和取整获取每一步计算结果。

Python实现

class Listnode(object):
    def __init__(self,x):
        self.val = x
        self.next = None

class Solution(object):
    def addtwonumbers(self,l1,l2):
        p = dummy = Listnode(-1)
        carry = 0
        while l1 and l2:
            p.next = Listnode(l1.val + l2.val + carry)
            carry = p.next.val // 10
            p.next.val %= 10
            p = p.next
            l1 = l1.next
            l2 = l2.next
        res = l1 or l2
        while res:
            p.next = Listnode(res.val + carry)
            carry = p.next.val // 10
            p.next.val %= 10
            p = p.next
            res = res.next
        if carry:
            p.next = Listnode(carry)
        return dummy.next
x = Listnode(2)
x.next = Listnode(4)
x.next.next = Listnode(3)
x.next.next.next = None
y = Listnode(5)
y.next = Listnode(6)
y.next.next = Listnode(4)
y.next.next.next = None
test = Solution()
dummy = test.addtwonumbers(x,y)
while(dummy):
    print(dummy.val)
    dummy = dummy.next

C++版本

#include <iostream>
#include <vector>
#include<unordered_map>
// Two Sum
using namespace std;
struct Lnode{
    int data;
    Lnode *next;
};
// 遍历并打印链表
void Ourlist(Lnode *head){ // 输入的是指针的复制,所以并不会改变外部指针的指向
    Lnode *pcur=head;
    while(pcur!=NULL){
        cout<<pcur->data<<"";
        pcur=pcur->next;
    }
    cout<<"\n"<<"";
}
class Solution{
public:
    Lnode *addtwonum(Lnode *l1,Lnode *l2){
        int x=0,y=0,carry=0,sum=0;
        Lnode *h=NULL,**t=&h;
        while(l1!=NULL || l2!=NULL){
            x=getvalue(l1);
            y=getvalue(l2);
            sum=carry+x+y;
            Lnode *node=new Lnode();
            node->data=sum%10;
            *t=node; // 指向node
            t=(&node->next); // 将node的next指针的地址赋给t
            carry=sum/10;
        }
        if(carry>0){
            Lnode *node=new Lnode();
            node->data=carry;
            *t=node;
        }
        return h;
    }
private:
    int getvalue(Lnode* &l){ // 这里&表示复制的是指针的本体
        int x=0;
        if(l!=NULL){
            x=l->data;
            l=l->next;
        }
    return x;
    }
    
};
int main(){
    Lnode *pl1,*pl2,*pout;
    vector<int> vp1={2,4,3};
    vector<int> vp2={5,6,4};
    Lnode *l1=(Lnode*)malloc(3*sizeof(Lnode));
    Lnode *l2=(Lnode*)malloc(3*sizeof(Lnode));
    for(int i=0;i<3;i++){
        l1[i].data=vp1[i];
        l1[i].next=&l1[i+1];
        l2[i].data=vp2[i];
        l2[i].next=&l2[i+1];
    }
    l1[2].next=NULL;
    l2[2].next=NULL;
    pl1=&l1[0];
    pl2=&l2[0];
    Ourlist(pl1);
    Ourlist(pl2);
    Lnode *(Solution::*pfunc)(Lnode *l1,Lnode *l2)=&Solution::addtwonum; 
    Solution test;
    pout=(test.*pfunc)(pl1,pl2);  // // pout = test.addtwonum(pl1,pl2)
    Ourlist(pout);
}

3.无重复的最长子串

给定一个字符串"abcabcbb",输出没有重复的最长子串"abc"的长度3。
算法思想:可以通过字典的方式,将已有的元素作为字典的索引,将其索引作为字典的值,这样如果出现相同的值,则会计算子串长度,同时也要考虑一个细微的情况,是每当计算一个重复元素后,需要将后续的计算从该重复元素后开始。

Python

class Solution(object):
    def longest1(self,s):
        num = len(s)
        maxlen = 0
        sub = ""
        for i in range(num):
            if s[i] not in sub:
                sub = sub + s[i]
                print(sub)
                if i==num-1:
                    lensub = len(sub)
                    maxlen = max(maxlen,lensub)
            else:
                j = sub.find(s[i])
                lensub = len(sub)-j
                maxlen = max(maxlen,lensub)
                sub = sub[j+1:]
                sub = sub +s[i]
                print(sub)
        print(maxlen)
        return maxlen
    def longest2(self,s):
        d = {}
        start = 0
        ans = 0
        for i,c in enumerate(s):
            if c in d:
                start = max(start,d[c]+1)  # max必须要有start保存着sub的起始位置
            d[c] = i
            ans = max(ans,i-start+1)  # 保存最大值
        print(ans)
        return ans
    def longest3(self,s):
        d = collections.defaultdict(int)
        l = ans = 0
        for i,c in enumerate(s):
            while(l>0) and d[c]>0:
                d[s[i-1]] -= 1
                l -= 1
            d[c] += 1
            l += 1
            ans = max(ans, l)
            print(ans)
            return ans

x = "bbcbcbaadef"
y = "abbdcbabcdfd"
test = Solution()
test.longest3(x)

C++版本

int lensub1(string s){
    map<char,int> m;
    int maxlen=0;
    int lastrepeat=-1;
    for(int i=0;i<s.size();i++){
        if(m.find(s[i])!=m.end() && lastrepeat<m[s[i]]){
            lastrepeat=m[s[i]];
        }
        if(i-lastrepeat>maxlen){
            maxlen=i-lastrepeat;
        }
        m[s[i]]=i;
    }
    return maxlen;
}
int lensub2(string s){
    const int MAX_CHARS=256;
    int m[MAX_CHARS];
    memset(m, -1, sizeof(m));  // 初始化m的所有元素值为-1
    int maxlen=0;
    int lastrepeat=-1;
    for(int i=0;i<s.size();i++){
        if(m[s[i]]!=-1&&lastrepeat<m[s[i]]){
            lastrepeat=m[s[i]];
        }
        if(i-lastrepeat>maxlen) {
            maxlen=i-lastrepeat;
        }
        m[s[i]]=i;
    }
    return maxlen;
}
int main(int argc,char** argv){
    string s="abcabcbb";
    cout<<s<<":"<<lensub1(s)<<endl;
    s="bbbb";
    cout<<s<<":"<<lensub2(s)<<endl;
    s="bbabcdb";
    cout<<s<<":"<<lensub2(s)<<endl;
    if (argc>1) {
        s=argv[1];
        cout<<s<<":"<<lensub2(s)<<endl;
    }
    return 0;
}

4.两个排序数组的中位数

给定两个大小m和n的有序数组,请找出两个数组的中位数,要求算法的复杂度为O(long(m+n))。
算法思想:1.找到索引相加为最终数组中位数索引值的两个数组的两个数。2.最终数组的元素个数影响着中位数的选取。

Python版本

def median1(A,B):
    m,n = len(A),len(B)
    if m>n:
        A,B,m,n = B,A,n,m
    if n ==0:
        raise ValueError
    imin,imax,half_len = 0,m,(m+n+1)//2
    while imin <= imax:
        i = (imin+imax)//2
        j = half_len-i
        if i<m and B[j-1]>A[i]:
            imin = i+1
        elif i>0 and A[i-1]>B[j]:
            imax = i-1
        else:
            if i==0: max_of_left = B[j-1]
            elif j==0: max_of_left = A[i-1]
            else:max_of_left = max(A[i-1],B[j-1])
            if(m+n)%2==1:
                return max_of_left
            if i==m: min_of_right = B[j]
            elif j==n: min_of_right = A[i]
            else: min_of_right = min(A[i],B[j])
            return (max_of_left + min_of_right)/2.0
def median2(A,B):
    a,b = sorted([A,B],key=len)
    m,n = len(a),len(b)
    after = (m+n-1)//2
    lo,hi = 0,m
    while lo<hi:
        i = (lo+hi)//2
        if after-i-1<0 or a[i]>=b[after-i-1]:
            hi=i
        else:
            lo = i+1
    i = lo
    nextfew = sorted(a[i:i+2]+b[after-i:after-i+2])
    return (nextfew[0]+nextfew[1-(m+n)%2])/2.0

mid = median2([2,4,6,8,9],[1,3,5,7])
print(mid)

C++版本的代码与python代码的第二种方法相同。

float median(int a[],int b[],int m,int n){
    if(m>n){
        int *p=a;
        int temp=n;
        a = b; b = p;
        n = m; m = temp;
        for (int i=0;i<m;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
        for(int i=0;i<n;i++){
            cout<<b[i]<<" ";
        }
    }
    int after = (m+n-1)/2;
    int lo=0,hi=m,i=0;
    while (lo<hi) {
        i = (lo+hi)/2;
        if((after-i-1<0)||(a[i]>=b[after-i-1])){
            hi = i;
        }
        else lo = i+1;
    }
    i = lo;
    int c[] = {a[i],a[i+1],b[after-i],b[after-i+1]};
    sort(c,c+4);
    cout<<endl;
    for(int i=0;i<4;i++){
        cout<<c[i]<<" ";
    }
    cout<<endl;
    return (m+n)%2==1 ? c[0] : (c[0]+c[1])/2.0;
}
int main(){
    int b[]={1,3,5,7};
    int a[]={2,4,6,8,9};
    int m=sizeof(a)/sizeof(a[0]);
    int n=sizeof(b)/sizeof(b[0]);
    float med = median(a, b, m, n);
    cout<<"中位数:"<<med<<endl;
    return 0;
}

5.最长回文子串

给定一个字符串s,找出s中的最长回文子串。
算法思想:常见的思路是,遍历每个元素和每个元素+下一个元素(回文中间可能为一个或两个相同的元素),找到最长回文子串。

Python实现

class Solution(object):
    def longestpal(self,s):
        left=right=0
        n=len(s)
        for i in range(n-1):
            if 2*(n-i)+1<right-left+1:
                break
            l=r=i
            while l>=0 and r<n and s[l]==s[r]:
                l -= 1
                r += 1
            if r-l-2>right-left:
                left = l+1
                right = r-1
            l=i
            r=i+1
            while l>=0 and r<n and s[l]==s[r]:
                l -= 1
                r += 1
            if r-l-2>right-left:
                left = l+1
                right = r-1
        return s[left:right+1]
test = Solution()
print(test.longestpal("cabba"))

C++ 版本与python版本思想相同

// int & 变量的引用
void findpal(string s,int left,int right,int& start,int& len){
    long n=s.size();
    while (left>=0&&right<=n-1&&s[left]==s[right]) {
        left--;
        right++;
    }
    if(right-left-1>len){
        len=right-left-1;
        start=left+1;
    }
}
string longestpal(string s){
    long n=s.size();
    if(n<=1) return s;
    int start=0,len=0;
    for(int i=0;i<n;i++){
        findpal(s,i,i,start,len);
        findpal(s,i,i+1,start,len);
    }
    return s.substr(start,len);
}
int main(){
    string s="cabba";
    string longest=longestpal(s);
    long n=longest.size();
    for(int i=0;i<n;i++){
        cout<<longest[i];
    }
    cout<<endl;
    return 0;
}

6.Z字形变换

给定一个字符串和变换的行数,输出变换后的字符串,如给定"PAYPALISHIRING",如果变换行数为4,则每一行为:
PIN
ALSIG
YAHR
PI
则输出"PINALSIGYAHRPI"
算法思想:这里提供两种思路,一是按照其规律构建一个包含string的向量,循环每个元素依次添加,C++实现;第二种是找到每一行构建的索引规律,循环每一行。

python实现

class Solution(object):
    def convert(self,s,nrows):
        if nrows<=1: return s
        n = len(s)
        ans = []
        step = 2*nrows - 2
        for i in range(nrows):
            one = i
            two = -i
            while one<n or two<n:
                if 0<=two<n and one !=two and i !=nrows-1:
                    ans.append(s[two])
                if one<n:
                    ans.append(s[one])
                one += step
                two += step
        return "".join(ans)
test = Solution()
print(test.convert("PAYPALISHIRING",4))
#索引规律
#0 6 12
#1 5 7 11 13
#2 4 8 10 14 
#3 9 15 

C++版本

string convert(string s,int nrows){
    if(nrows<=1 || nrows>=s.size()) return s;
    vector<string> r(nrows);
    int row = 0;
    int step = 1;
    for(int i=0;i<s.size();i++){
        if(row==nrows-1) step=-1;
        if(row==0) step=1;
        r[row] += s[i];
        row += step;
    }
    string result;
    for(int i=0;i<nrows;i++){
        result += r[i];
    }
    return result;
}
int main(int argc,char**argv){
    string s;
    int r;
    s = "PAYPALISHIRING";
    r = 4;
    cout<<s<<":"<<convert(s, r)<<endl;
}

7.字符串转数字(atoi)

将字符串转为整数,如果不能有效的转换返回0,这里数字可能会有“+” “-”号。
算法思想:数据类型判断好即可,C++指针的运用可以使代码精简。

Python实现

"""8.字符串转整数(atoi)"""
def myatoi(s):
    s = s.strip()
    sigh = 1
    if s[0] in ["+","-"]:
        if s[0]=="-":
            sigh = -1
        s = s[1:]
    ans = 0
    for c in s:
        if c.isdigit():
            ans = ans*10 +int(c)
        else:
            break
    ans *= sigh
    if ans > 2147483647:
        return 2147483647
    if ans < -2147483648:
        return -2147483648
    return ans

print(myatoi("-33"))
print(myatoi("43"))

C++实现

int atoi(const char *str){
    if (str==NULL || *str=='\0'){
        return 0;
    }
    int ret=0;
    for(;isspace(*str);str++);
    bool neg=false;
    if(*str=='-'||*str=='+'){
        neg=(*str=='-');
        str++;
    }
    for(;isdigit(*str);str++){

        int digit=(*str-'0'); // -'0'避免取ascii码值
        if(neg){
            if(-ret<(INT_MIN+digit/10)){
                return INT_MIN;
            }
        }
        else{
            if(ret>(INT_MAX-digit)/10){
                return INT_MAX;
            }
        }
        ret = ret*10 + digit;
    }
    return neg?-ret:ret;
}
int main(){
    cout<<(atoi("-34ww"))<<endl;
}

9.判断是否是回文数

给出一个整数,判断这个整数正序和倒序读是否一致。
算法思想:可以将其转化为字符串处理,也可以将整数翻转来判断是否一致,这里还有一种方法取整数一半做判断。

def ispal1(num):
    if num<0: return False
    s = str(num)
    left = 0
    right = len(s)-1
    while(left<right):
        if(s[left]!=s[right]):
            return False
        left += 1
        right -= 1
    return True
print(ispal1(121))
print(ispal1(12))
print(ispal1(-2))

def ispal2(num):
    if num<0 or (num !=0 and num % 10==0):
        return False
    half = 0
    while num > half:
        half = half * 10 + num % 10
        num =num // 10
    return half==num or (half//10)==num

print(ispal2(121))
print(ispal2(12))
print(ispal2(-2))

C++版本

class Solution{
public:
    bool ispalindrome(int x){
        if(x<0){
            return false;
        }
        int len=1;
        for(len=1;(x/len)>=10;len *=10);
        while(x!=0){
            int left=x/len;
            int right =x%10;
            if(left!=right){
                return false;
            }
            x = (x%len)/10;
            len /=100;
        }
        return true;
    }
    bool ispalindrome2(int x){
        return (x>0&&x==reverse(x));
    }
private:
    int reverse(int x){
        int y=0;
        int n;
        while(x!=0){
            n = x%10;
            y = y*10 + n;
            x /=10;
        }
        return y;
    }
};
int main(){
    Solution s;
    cout<<s.ispalindrome(123)<<endl;
    cout<<s.ispalindrome2(11)<<endl;
}

10.正则表达式匹配

给定一个字符串(s)和一个字符模式§实现支持’.‘和’*'的正则表达式匹配

  • '.'匹配任意单个字符,这里在样例貌似有点歧义,代码实现是按照任意单个字符,而不是单个或多个字符
  • '*'匹配零个或多个前面的元素
  • s可能为空,且只包含a-z的小写字母
  • p可能为空,且只包含从a-z的小写字母,以及字符.和*
    s = ‘aa’ 匹配 p = ‘a*’
    s = ‘ab’ 匹配 p = ‘.’
    s = ‘aab’ 匹配 p = ‘cab’ //这里c匹配零个,a匹配两个

算法思想
Python算法我看的有点懵,实际是构建了一个对应的矩阵。
C++最大的不确定因素为’’,所以将考虑情况分为两种,当p的后一位不是’’,只需要循环调用自身即可;当p的后一位是’’,则需要尝试’'所重复的数目,总之,两种情况都需要递归算法。

Python版本

def ismatch(s,p):
    dp = [[False]*(len(p)+1) for _ in range(len(s)+1)]
    dp[0][0] = True
    for j in range(1,len(p)+1):
        if p[j-1] == "*":
            dp[0][j] = dp[0][j-2]
    for i in range(1,len(s)+1):
        for j in range(1,len(p)+1):
            if p[j-1] != "*":
                dp[i][j] = dp[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == ".")
            else:
                # 前一步为True and 当前步的两种情况满足一种即可(p中当前元素前一个即j-2与i-1相同或者当前元素后一个j与i-1相同)
                # 但是这里代码的两种情况,与上述描述有点出入,我再想想...
                dp[i][j] = dp[i][j-2] or dp[i-1][j] and (p[j-2] == s[i-1] or p[j-2] == ".")
    return dp[-1][-1]

print(ismatch("aaabc","a*.c"))
print(ismatch("abc","aa*bc"))
print(ismatch("aac","a*c"))
print(ismatch("abc","aac"))

C++版本

bool ismatch(const char *s,const char *p){
    if(*p=='\0'){
        return *s=='\0';
    }
    // 当前不含有*的情况,只需要调用递归算法
    if (*(p+1)=='\0'||*(p+1)!='*'){
        if(*s=='\0'||(*p!='.'&&*s!=*p)){
            return false;
        }
        return ismatch(s+1, p+1);
    }
    // 当有p+1=‘*’,需要尝试得到*重复的次数
    int len = strlen(s);
    int i=-1;
    while (i<len&&(i<0||*p=='.'||*p==*(s+i))) {
        if(ismatch(s+i+1, p+2)){ //尝试*重复的数目:从0到最大len个
        return true;
        }
        i++;
    }
    return false;
}
int main(int argc, char** argv){
    const char* s="aaa";
    const char* p="a.*";
    if (argc>2){
        s = argv[1];
        p = argv[2];
    }
    cout<<s<<","<<p<<":"<<ismatch(s, p)<<endl;
}

本文代码可能有bug,还请各位看官指正,或者小伙伴们有更好的算法,欢迎评论讨论~
后续算法请查看 算法(二)

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书用Python语言来讲解算法的分析和设计 分别介绍了树、图、计数问题、归纳递归、遍历、分解合并、贪心算法、复杂依赖、Dijkstra算法、匹配切割问题以及困难问题及其稀释等内容 Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it also gives a solid understanding of fundamental algorithmic problem-solving techniques. * The book deals with some of the most important and challenging areas of programming and computer science, but in a highly pedagogic and readable manner. * The book covers both algorithmic theory and programming practice, demonstrating how theory is reflected in real Python programs. * Well-known algorithms and data structures that are built into the Python language are explained, and the user is shown how to implement and evaluate others himself. What you'll learn * Transform new problems to well-known algorithmic problems with efficient solutions, or show that the problems belong to classes of problems thought not to be efficiently solvable. * Analyze algorithms and Python programs both using mathematical tools and basic experiments and benchmarks. * Prove correctness, optimality, or bounds on approximation error for Python programs and their underlying algorithms. * Understand several classical algorithms and data structures in depth, and be able to implement these efficiently in Python. * Design and implement new algorithms for new problems, using time-tested design principles and techniques. * Speed up implementations, using a plethora of tools for high-performance computing in Python. Who this book is for The book is intended for Python programmers who need to learn about algorithmic problem-solving, or who need a refresher. Students of computer science, or similar programming-related topics, such as bioinformatics, may also find the book to be quite useful. Table of Contents * Introduction * The Basics * Counting 101 * Induction and Recursion ...and Reduction * Traversal: The Skeleton Key of Algorithmics * Divide, Combine, and Conquer * Greed Is Good? Prove It! * Tangled Dependencies and Memoization * From A to B with Edsger and Friends * Matchings, Cuts, and Flows * Hard Problems and (Limited) Sloppiness

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值