专题3 栈和队列 + 数学和字符串
专题3-1 栈和队列
1. 栈的压入弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
Python
2.包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
Python:
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack = []
self.minvalue = []
def push(self, node):
# write code here
self.stack.append(node)
if self.minvalue:
if self.minvalue[-1] > node:
self.minvalue.append(node)
else:
self.minvalue.append(self.minvalue[-1])
else:
self.minvalue.append(node)
def pop(self):
# write code here
if self.stack == []:
return None
self.minvalue.pop()
return self.stack.pop()
def top(self):
# write code here
if self.stack == []:
return None
return self.stack[-1]
def min(self):
# write code here
if self.minvalue == []:
return None
return self.minvalue[-1]
3.用两个栈来实现一个队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
Python
# -*- coding:utf-8 -*-
##队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头
#栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素
class Solution:
def init(self):
#添加数据栈
self.acceptStack=[]
#删除数据栈
self.outputStack = []
def push(self, node):
#向添加数据的栈中添加数据
self.acceptStack.append(node)
def pop(self):
#判断删除数据的栈中是否有数据,没有的话,就添加数据,添加数据时,要添加栈1 中删除的数据
if not self.outputStack:
while self.acceptStack:
self.outputStack.append(self.acceptStack.pop())
#如果有数据的话,就返回
if self.outputStack:
return self.outputStack.pop()
#如果没有数据,说明没有数据添加进去,也就不需要删除数据,所以返回none
else:
return None
专题3-2 字符串和数学
1.字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
Python:
# -*- coding:utf-8 -*-
class Solution:
# 返回对应char
def __init__(self):
self.s = ""
def FirstAppearingOnce(self):
# write code here
#method1
res=list(filter(lambda c:self.s.count(c)==1,self.s))
return res[0] if res else "#"
'''
#method2 python3
ret = []
res = {x: self.s.count(x) for x in self.s}
for key, value in res.items():
if value == 1:
ret.append(key)
return ret[0] if ret else "#"
#method3
for a in self.s:
if self.s.count(a) == 1:
return a
break
return '#'
'''
def Insert(self, char):
# write code here
self.s += char
2.表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
Python:
# -*- coding:utf-8 -*-
class Solution:
# s字符串
def isNumeric(self, s):
# write code here
if len(s) <= 0:
return False
# 分别标记是否出现过正负号、小数点、e,因为这几个需要特殊考虑
has_sign = False
has_point = False
has_e = False
for i in range(len(s)):
# 对于e的情况
if s[i] == 'E' or s[i] == 'e':
# 不同出现两个e
if has_e:
return False
# e不能出现在最后面,因为e后面要接数字
else:
has_e = True
if i == len(s) -1:
return False
# 对于符号位的情况
elif s[i] == '+' or s[i] == '-':
# 如果前面已经出现过了符号位,那么这个符号位,必须是跟在e后面的
if has_sign:
if s[i-1] != 'e' and s[i-1] != 'E':
return False
# 如果这是第一次出现符号位,而且出现的位置不是字符串第一个位置,那么就只能出现在e后面
else:
has_sign = True
if i > 0 and s[i-1] != 'e' and s[i-1] != 'E':
return False
# 对于小数点的情况
elif s[i] == '.':
# 小数点不能出现两次;而且如果已经出现过e了,那么就不能再出现小数点,因为e后面只能是整数
if has_point or has_e:
return False
# 如果是第一次出现小数点,如果前面出现过e,那么还是不能出现小数点
else:
has_point = True
if i > 0 and (s[i-1] == 'e' or s[i-1] == 'E'):
return False
else:
# 其他字符必须是‘0’到‘9’之间的
if s[i] < '0' or s[i] > '9':
return False
return True
'''
##method2:
try:
ss=float(s)
return True
except:
return False
'''
3.正则表达式的匹配
题目描述
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
Python:
# -*- coding:utf-8 -*-
class Solution:
# s, pattern都是字符串
def match(self, s, pattern):
# write code here
if len(s) == 0 and len(pattern) == 0:
return True
elif len(s) != 0 and len(pattern) == 0:
return False
elif len(s) == 0 and len(pattern) != 0:
# pattern中的第二个字符为*,则pattern后移两位继续比较
if len(pattern) > 1 and pattern[1] == '*':
return self.match(s, pattern[2:])
else:
return False
#s与pattern都不为空的情况
else:
# pattern的第二个字符为*的情况
if len(pattern) > 1 and pattern[1] == '*':
# s与pattern的第一个元素不同,则s不变,pattern后移两位,相当于pattern前两位当成空
if s[0] != pattern[0] and pattern[0] != '.':
return self.match(s, pattern[2:])
else:
#如果s[0]与pattern[0]相同,且pattern[1]为*,这个时候有三种情况
#pattern后移2个,s不变;相当于把pattern前两位当成空,匹配后面的
# pattern后移2个,s后移1个;相当于pattern前两位与s[0]匹配
# pattern不变,s后移1个;相当于pattern前两位,与s中的多位进行匹配,因为*可以匹配多位
return self.match(s, pattern[2:]) or self.match(s[1:], pattern[2:]) or self.match(s[1:], pattern)
# pattern第二个字符不为*的情况
else:
if s[0] == pattern[0] or pattern[0] == '.':
return self.match(s[1:], pattern[1:])
else:
return False
C++
class Solution {
public:
/* 注意,'.'是任意一个非空字符
解这题需要把题意仔细研究清楚,反正我试了好多次才明白的。
首先,考虑特殊情况:
1>两个字符串都为空,返回true
2>当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成功的,比如第二个字符串是“a*a*a*a*”,由于‘*’之前的元素可以出现0次,所以有可能匹配成功),之后就开始匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern,下一个字符可能是‘*’, 这里我们分两种情况讨论:pattern下一个字符为‘*’或不为‘*’:
1>pattern下一个字符不为‘*’:这种情况比较简单,直接匹配当前字符。如果匹配成功,继续匹配下一个;如果匹配失败,直接返回false。注意这里的“匹配成功”,除了两个字符相同的情况外,还有一种情况,就是pattern的当前字符为‘.’,同时str的当前字符不为‘0’。
2>pattern下一个字符为‘*’时,稍微复杂一些,因为‘*’可以代表0个或多个。这里把这些情况都考虑到:
a>当‘*’匹配0个字符时,str当前字符不变,pattern当前字符后移两位,跳过这个‘*’符号;
b>当‘*’匹配1个或多个时,str当前字符移向下一个,pattern当前字符不变。(这里匹配1个或多个可以看成一种情况,因为:当匹配一个时,
由于str移到了下一个字符,而pattern字符不变,就回到了上边的情况a;当匹配多于一个字符时,相当于从str的下一个字符继续开始匹配)
之后再写代码就很简单了。
*/
bool match(char* str, char* pattern)
{
if (*str == '0' && *pattern == '0')
return true;
if (*str != '0' && *pattern == '0')
return false;
//if the next character in pattern is not '*'
if (*(pattern+1) != '*')
{
if (*str == *pattern || (*str != '0' && *pattern == '.'))
return match(str+1, pattern+1);
else
return false;
}
//if the next character is '*'
else
{
if (*str == *pattern || (*str != '0' && *pattern == '.'))
return match(str, pattern+2) || match(str+1, pattern);
else
return match(str, pattern+2);
}
}
};
4.把字符串转换为整数
题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
Python:
# -*- coding:utf-8 -*-
class Solution:
def StrToInt(self, s):
# write code here
numlist=['0','1','2','3','4','5','6','7','8','9','+','-']
sum=0
label=1#正负数标记
if s=='':
return 0
for string in s:
if string in numlist:#如果是合法字符
if string=='+':
label=1
continue
if string=='-':
label=-1
continue
else:
sum=sum*10+numlist.index(string)
if string not in numlist:#非合法字符
sum=0
break#跳出循环
return sum*label
5.不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
Python:
# -*- coding:utf-8 -*-
class Solution:
def Add(self, num1, num2):
# write code here
xornum = num1 ^ num2
andnum = (num1 & num2) << 1
while andnum != 0:
tmp1 = xornum ^ andnum
tmp2 = (xornum & andnum) << 1
tmp1 = tmp1 & 0xFFFFFFFF #越界检查 32位
xornum = tmp1
andnum = tmp2
#正数返回,负数的话还原~n = -(n+1)
return xornum if xornum <= 0x7FFFFFFF else ~(xornum^0xFFFFFFFF)
C++
class Solution {
public:
int Add(int num1, int num2)
{
int xornum = num1 ^ num2;
int andnum = (num1 & num2) << 1;
while(andnum != 0)
{
int tmp1 = xornum ^ andnum;
int tmp2 = (xornum & andnum) << 1;
xornum = tmp1;
andnum = tmp2;
}
return xornum;
}
};
6.求1+2+3+...+n
题目描述
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
Python:
# -*- coding:utf-8 -*-
class Solution:
def Sum_Solution(self, n):
# write code here
#return sum(list(range(1,n+1)))
return (pow(n,2) + n) >> 1
7.翻扑克牌子
题目描述
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
Python:
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
#method1
if len(numbers):
while min(numbers) == 0:
numbers.remove(0)
if max(numbers) - min(numbers) <= 4 and len(numbers) == len(set(numbers)):
return True
return False
C++
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
if(numbers.size()!=5) return false;
sort(numbers.begin(),numbers.end());
int i=0;
while(numbers[i]==0) i++;
if(numbers[4]-numbers[i]>4) return false;
for(int j=i;j<4;j++){
if(numbers[j]==numbers[j+1]) return false;
}
return true;
}
};
8.翻转单词序列
题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
Python:
# -*- coding:utf-8 -*-
class Solution:
def ReverseSentence(self, s):
# write code here
if len(s) == 0:
return s
s = s.split(" ")
#print(s[::-1])
res = " ".join(s[::-1])
return res
9.左旋转字符串
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
Python:
# -*- coding:utf-8 -*-
class Solution:
def LeftRotateString(self, s, n):
# write code here
l = len(s)
if l <= 0 or n < 0:
return s
n = n % l
a = s[:n][::-1]
b = s[n:][::-1]
c = b[::-1] + a[::-1]
return c
'''
return s[n:] + s[:n]
'''
10.第一个只出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
Python
# -*- coding:utf-8 -*-
class Solution:
def FirstNotRepeatingChar(self, s):
# write code here
#return [i for i in range(len(s)) if s.count(s[i])==1][0] if s else -1
'''
if s:
a = [i for i in range(len(s)) if s.count(s[i])==1][0]
return a
else:
return -1
'''
#判断输入条件
if len(s)<=0 or len(s)>10000:
return -1
#count用于统计字符串中某个字符的出现]数
#index为计算字符串中某个字符的位置
for i in s:
if s.count(i)==1:
return s.index(i)
break
11.数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
Python
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
#return base**exponent
if base == 0:
return 0
if exponent == 0:
return 1
p = abs(exponent)
res = 1
while(p > 0):
#如果最后一位为1,那么给res乘上这一位的结果
if (p & 1 == 1):
res =res * base
p = p >> 1
base = base * base
return res if exponent > 0 else 1/res
C++
class Solution {
public:
double Power(double base, int exponent)
{ //code
if(base == 0.0)
return 0;
if(exponent == 0.0)
return 1;
long long p = abs((long long)exponent);
double r = 1.0;
while(p){
if(p & 1) r *= base;
base *= base;
p >>= 1;
}
return exponent < 0 ? 1/ r : r;
}
};
12.二进制中1的个数
题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示
Python:
def NumberOf1(self, n):
# write code here
#flag = 1
count = 0
if n < 0: ##必须加
n = n & 0xffffffff #为了获得负数(十进制表示)的补码,需要手动将其和十六进制数0xfffffffd进行按位与操作
while n:
count += 1
n = (n-1)&n
return count
C++
class Solution {
public:
int NumberOf1(int n) {
/*
int count = 0;
int flag = 1;
//C语言中 << 是逻辑移位,不是循环移位; 1左移32位后为0
while(flag)
{
if(n&flag)
count++;
flag = flag << 1;
}
return count;
*/
int count = 0;
while(n)
{
count ++;
n = (n-1) & n;//消除n最右边的一个0
}
return count;
}
};
13.替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
Python
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
#s1 = s.replace(' ', '%20')
#return s1
new_s = ''
for j in s:
if j == ' ':
new_s = new_s + '%20'
else:
new_s = new_s + j
return new_s
C++
class Solution {
public:
void replaceSpace(char *str,int length)
{
if(str==NULL||length<0)
return;
int i=0;
int oldnumber=0;//记录以前的长度
int replacenumber=0;//记录空格的数量
while(str[i]!='0')
{
oldnumber++;
if(str[i]==' ')
{
replacenumber++;
}
i++;
}
int newlength=oldnumber+replacenumber*2;//插入后的长度
if(newlength>length)//如果计算后的长度大于总长度就无法插入
return;
int pOldlength=oldnumber; //注意不要减一因为隐藏个‘0’也要算里
int pNewlength=newlength;
while(pOldlength>=0&&pNewlength>pOldlength)//放字符
{
if(str[pOldlength]==' ') //碰到空格就替换
{
str[pNewlength--]='0';
str[pNewlength--]='2';
str[pNewlength--]='%';
}
else //不是空格就把pOldlength指向的字符装入pNewlength指向的位置
{
str[pNewlength--]=str[pOldlength];
}
pOldlength--; //不管是if还是elsr都要把pOldlength前移
}
}
};
14.整数中出现1的次数(从1到n)******
题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
Python
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
#return ''.join(map(str, range(n+1))).count('1')
#method 1
# 将1-n全部转换为字符串
# 只需要统计每个字符串中'1'出现的次数并相加即可
return ''.join(map(str,range(n+1))).count('1')
C++
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
//方法1:暴力求解
//int count = 0;
// for(int i=0; i<=n; i++){
// int temp = i;
// while(temp){
// if(temp%10 == 1){
// count++;
// }
// temp /= 10;
// }
//}
//return count;
//方法2
int count = 0;
long long m = 1;
for(m=1; m<=n; m*=10)
{
int a = n/m;
int b = n%m;
count += (a+8)/10*m + (a%10==1)*(b+1);
}
return count;
}
};
//举一反三:如果求2的个数
//count += (a+7)/10*m + (a%10==2)*(b+1); 以此类推
C++方法2举例说明
当n = 3141592时:
当然后面还有m=10000,100000,1000000三种情况,对应着万位,十万位, 百万位为1时的情况
下面说下a+8的意义:
当考虑个位,十位,百位这三位为1的情况时:
个位 2 ,当个位取值1时,前面的六位数字可由0~314159组成,即314160种情况
十位9,当十位取值1时,前面的五位数字可由0~31415组成,十位之后的一位可由0~9组成,组合情况31416*10=314160种情况
百位5,当百位取值为1时,前面的四位数字可由0~3141组成,百位之后的两位可由0~99组成,组合情况为3142*100=314200种情况
注意:当考虑千位1时:
千位1,千位取值即1,前面的三位数字可由0~314组成,但是当前面的值为314时,后面的三位只有0~592种情况(特殊情况),其余的情况即为前面的值为0~313,后面三位有0~999,情况数为3141000,所以总情况数为3141000 + 593=314593种情况
这时可发现和代码中的公式算的情况是吻合的,a+8的巧妙之处在于当a的最后一位(当前分析位)为0或1时,加8不产生进位,这是为需要单独算的特殊情况做准备,而当前分析位为2~9时,不需要考虑特殊情况,所以允许加8产生的进位。(>2或者=0时计算相同,=1时计算附加值)