一、数位运算 题目
给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。
注意:
十六进制中所有字母(a-f)都必须是小写。
十六进制字符串中不能包含多余的前导零。如果要转化的数为0,那么以单个字符'0'来表示;对于其他情况,十六进制字符串中的第一个字符将不会是0字符。
给定的数确保在32位有符号整数范围内。
不能使用任何由库提供的将数字直接转换或格式化为十六进制的方法。
string toHex(int num){
if(num==0){
return "0";
}
string sb;
for(int i=7;i>=0;i--){
int val = (num>>(4*i)) & 0xf;
if(sb.length()>0 || val>0){
char dight = val < 10? ('0'+val) : (char)('a'+val-10);
sb.push_back(dight);
}
}
return sb;
}
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。你可以不使用额外空间来实现吗?
int singleNumber(vector<int>& nums) {
int res = 0;
for (auto num: nums) res ^= num;
return res;
}
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现三次。找出那个只出现了一次的元素。
我们处理的数据都是32位的数据。如果把每一个数字都看成二进制,那么一个十进制数nums[i]对应一个32位的二进制数。将所有nums[i]对应的二进制数的对应位求和,将每一对应位的和值与3进行取模运算,得到的余数就是答案的对应二进制位的数值。这是因为除了答案本身,其它元素都是三个三个为一组的。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < 32; i++) {
int sum = 0;
for (int num : nums) {
sum += ((num >> i) & 1);
}
if (sum % 3 == 1) {
res |= (1 << i);
}
}
return res;
}
};
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按任意顺序返回答案。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
unsigned int target = 0;
for (auto n: nums) {
target ^= n;
}
int lowbit = target & (-target);
int a1 = 0;
int a2 = 0;
for (auto n: nums) {
if (n & lowbit) {
a1 ^= n;
} else {
a2 ^= n;
}
}
vector<int> ans;
ans.push_back(a1);
ans.push_back(a2);
return ans;
}
};
二、字符串
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串
1.横向扫描
class Solution{
public:
string longestCommonPrefix(vector<string> &str){
if(!str.size()){
return "";
}
string s1=str[0];
int count=str.size();
for(int i=0;i<count;i++){
s1=longestCommonPrefix(s1,str[i]);
if(!s1.size()){
break;
}
}
return s1;
}
string longestCommonPrefix(string &str1,string &str2){
int length = min(str1.size(),str2.size());
int index=0;
while(index<length&&str1[index]==str2[index]){
++index;
}
return str1.substr(0,index);
}
};
2.纵向扫描
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) {
return "";
}
int length = strs[0].size();
int count = strs.size();
for (int i = 0; i < length; ++i) {
char c = strs[0][i];
for (int j = 1; j < count; ++j) {
if (i == strs[j].size() || strs[j][i] != c) {
return strs[0].substr(0, i);
}
}
}
return strs[0];
}
};
3.分治法
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) {
return "";
}
else {
return longestCommonPrefix(strs, 0, strs.size() - 1);
}
}
string longestCommonPrefix(const vector<string>& strs, int start, int end) {
if (start == end) {
return strs[start];
}
else {
int mid = (start + end) / 2;
string lcpLeft = longestCommonPrefix(strs, start, mid);
string lcpRight = longestCommonPrefix(strs, mid + 1, end);
return commonPrefix(lcpLeft, lcpRight);
}
}
string commonPrefix(const string& lcpLeft, const string& lcpRight) {
int minLength = min(lcpLeft.size(), lcpRight.size());
for (int i = 0; i < minLength; ++i) {
if (lcpLeft[i] != lcpRight[i]) {
return lcpLeft.substr(0, i);
}
}
return lcpLeft.substr(0, minLength);
}
};
4.
class Solution {
public:
string longestCommonPrefix(vector<string>& str) {
sort(str.begin(),str.end());
string &s1=str.front();
string &s2=str.back();
int i=0;
while(i<s1.size()&&i<s2.size()&&s1[i]==s2[i]){
++i;
}
return string(s1.begin(),s1.begin()+i);
}
};
反转字符串
1.双指针
class Solution {
public:
void reverseString(vector<char>& s) {
int n = s.size();
for (int left = 0, right = n - 1; left < right; ++left, --right) {
swap(s[left], s[right]);
}
}
};
2.位运算
class Solution{
public:
void reverseString(vector<char>& s){
int n = s.size();
int size = n;
for(int i=0;i<n/2;i++){
size--;
s[i] ^= s[size];
s[size] ^= s[i];
s[i] ^= s[size];
}
}
};
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
class Solution {
public:
string addStrings(string num1, string num2) {
int i = num1.length() - 1, j = num2.length() - 1, add = 0;
string ans = "";
while (i >= 0 || j >= 0 || add != 0) {
int x = i >= 0 ? num1[i] - '0' : 0;
int y = j >= 0 ? num2[j] - '0' : 0;
int result = x + y + add;
ans.push_back('0' + result % 10);
add = result / 10;
i -= 1;
j -= 1;
}
reverse(ans.begin(), ans.end());
return ans;
}
};
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
输出为:PAHNAPLSIIGYIP
class Solution{
public:
string convert(string s, int numRows) {
if(numRows == 1){
return s;
}
vector<string> result(min(numRows,int(s.size())));
int curRow = 0;
bool change = -1;
for(char c:s){
result[curRow] += c;
if(curRow==0||curRow=numRows-1){
change = !change;
}
curRow += change? 1 : -1;
}
string ret;
for(string row:rows){
ret+=row;
}
return ret;
}
};
某个字符串 S 需要执行一些替换操作,用新的字母组替换原有的字母组(不一定大小相同)。
每个替换操作具有 3 个参数:起始索引 i,源字 x 和目标字 y。规则是:如果 x 从原始字符串 S 中的位置 i 开始,那么就用 y 替换出现的 x。如果没有,则什么都不做。
举个例子,如果 S = “abcd” 并且替换操作 i = 2,x = “cd”,y = “ffff”,那么因为 “cd” 从原始字符串 S 中的位置 2 开始,所以用 “ffff” 替换它。
再来看 S = “abcd” 上的另一个例子,如果一个替换操作 i = 0,x = “ab”,y = “eee”,以及另一个替换操作 i = 2,x = “ec”,y = “ffff”,那么第二个操作将不会执行,因为原始字符串中 S[2] = 'c',与 x[0] = 'e' 不匹配。
所有这些操作同时发生。保证在替换时不会有任何重叠: S = "abc", indexes = [0, 1], sources = ["ab","bc"] 不是有效的测试用例。
输入:S = "abcd", indexes = [0,2], sources = ["ab","ec"], targets = ["eee","ffff"]
输出:"eeecd"
解释:
"ab" 从 S 中的索引 0 开始,所以它被替换为 "eee"。
"ec" 没有从原始的 S 中的索引 2 开始,所以它没有被替换
class Solution {
public:
string findReplaceString(string s, vector<int>& indices, vector<string>& sources, vector<string>& targets) {
int n = indices.size();
for(int i=0;i<n;i++){
if(s.find(sources[i],indices[i])==indices[i]){
s.replace(s.begin()+indices[i],s.begin()+indices[i]+sources[i].size(),targets[i]);
for(int j=i+1;j<n;j++){
if(indices[j]>indices[i]){
indices[j]=indices[j]-sources[i].size()+targets[i].size();
}
}
}
}
return s;
}
};
三、队列
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间
int leastInterval(vector<char>& tasks, int n) {
int len=tasks.size();
vector<int> vec(26);
for(char c:tasks) ++vec[c-'A'];
sort(vec.begin(),vec.end(),[](int& x,int&y){return x>y;});
int cnt=1;
while(cnt<vec.size()&&vec[cnt]==vec[0]) cnt++;
return max(len,cnt+(n+1)*(vec[0]-1) );
}
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
class MyCircularQueue {
public:
int *queue;
int front=0,rear=0;
int maxsize;
MyCircularQueue(int k) {
queue=new int[k+1];
maxsize=k+1;
}
bool enQueue(int value) {
if(isFull())return false;
rear=(rear+1)%maxsize;
queue[rear]=value;
return true;
}
bool deQueue() {
if(isEmpty())return false;
front=(front+1)%maxsize;
return true;
}
int Front() {
if(isEmpty())return -1;
return queue[(front+1)%maxsize];
}
int Rear() {
if(isEmpty())return -1;
return queue[rear];
}
bool isEmpty() {
return front==rear;
}
bool isFull() {
return ((rear+1)%maxsize==front);
}
};
设计实现双端队列。
你的实现需要支持以下操作:
MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。
#include <iostream>
#include <vector>
using namespace std;
class MyCircularDeque {
private:
vector<int> arr;
int front;
int rear;
int capacity;
public:
MyCircularDeque(int k) {
capacity = k + 1;
arr.assign(capacity, 0);
front = 0;
rear = 0;
}
bool insertFront(int value) {
if (isFull()) {
return false;
}
front = (front - 1 + capacity) % capacity;
arr[front] = value;
return true;
}
bool insertLast(int value) {
if (isFull()) {
return false;
}
arr[rear] = value;
rear = (rear + 1) % capacity;
return true;
}
bool deleteFront() {
if (isEmpty()) {
return false;
}
front = (front + 1) % capacity;
return true;
}
bool deleteLast() {
if (isEmpty()) {
return false;
}
rear = (rear - 1 + capacity) % capacity;
return true;
}
int getFront() {
if (isEmpty()) {
return -1;
}
return arr[front];
}
int getRear() {
if (isEmpty()) {
return -1;
}
return arr[(rear - 1 + capacity) % capacity];
}
bool isEmpty() {
return front == rear;
}
bool isFull() {
return (rear + 1) % capacity == front;
}
};
四、栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
?
注意:
你只能使用队列的基本操作 —— 也就是?push to back、peek/pop from front、size 和?is empty?这些操作。
你所使用的语言也许不支持队列。?你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列?, 只要是标准的队列操作即可。
?
class MyStack {
public:
queue<int> que1;
queue<int> que2;
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
int size = que1.size();
size--;
while (size--) {
que2.push(que1.front());
que1.pop();
}
int result = que1.front();
que1.pop();
que1 = que2;
while (!que2.empty()) {
que2.pop();
}
return result;
}
int top() {
return que1.back();
}
bool empty() {
return que1.empty();
}
};
一个队列:
class MyStack {
public:
queue<int> que;
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size();
size--;
while (size--) {
que.push(que.front());
que.pop();
}
int result = que.front();
que.pop();
return result;
}
int top() {
return que.back();
}
bool empty() {
return que.empty();
}
};
给定一个只包括 '(',')','{','}','[',']'?的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
char pairs(char a) {
if (a == '}') return '{';
if (a == ']') return '[';
if (a == ')') return '(';
return 0;
}
bool isValid(char* s) {
int n = strlen(s);
if (n % 2 == 1) {
return false;
}
int stk[n + 1], top = 0;
for (int i = 0; i < n; i++) {
char ch = pairs(s[i]);
if (ch) {
if (top == 0 || stk[top - 1] != ch) {
return false;
}
top--;
} else {
stk[top++] = s[i];
}
}
return top == 0;
}
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop()?—— 删除栈顶的元素。
top()?—— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
class MinStack {
stack<int> x_stack;
stack<int> min_stack;
public:
MinStack() {
min_stack.push(INT_MAX);
}
void push(int x) {
x_stack.push(x);
min_stack.push(min(min_stack.top(), x));
}
void pop() {
x_stack.pop();
min_stack.pop();
}
int top() {
return x_stack.top();
}
int getMin() {
return min_stack.top();
}
};
二叉树的前序遍历
1.递归
void preorder(struct TreeNode* root, int* res, int* resSize) {
if (root == NULL) {
return;
}
res[(*resSize)++] = root->val;
preorder(root->left, res, resSize);
preorder(root->right, res, resSize);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = malloc(sizeof(int) * 2000);
*returnSize = 0;
preorder(root, res, returnSize);
return res;
}
2.栈
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = malloc(sizeof(int) * 2000);
*returnSize = 0;
if (root == NULL) {
return res;
}
struct TreeNode* stk[2000];
struct TreeNode* node = root;
int stk_top = 0;
while (stk_top > 0 || node != NULL) {
while (node != NULL) {
res[(*returnSize)++] = node->val;
stk[stk_top++] = node;
node = node->left;
}
node = stk[--stk_top];
node = node->right;
}
return res;
}
给出一个字符串?s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。
注意,您的结果中 不应 包含任何括号。
输入:s = "(abcd)"
输出:"dcba"
输入:s = "(u(love)i)"
输出:"iloveu"
解释:先反转子字符串 "love" ,然后反转整个字符串。
Cpp:
string reverses(string s){
stack<string> stk;
string str;
for(auto& ch : s)
{
if(ch == '(')
{
stk.push(str);
str = "";
}
else if(ch == ')')
{
reverse(str.begin(), str.end());
str = stk.top() + str;
stk.pop();
}
else
str += ch;
}
return str;
}
C:
void reverse(char* str, int strSize) {
for (int j = 0; j < strSize / 2; j++) {
char tmp = str[j];
str[j] = str[strSize - j - 1];
str[strSize - j - 1] = tmp;
}
}
char* reverseParentheses(char* s) {
int n = strlen(s);
char* stk[n];
int top = 0;
char* str = malloc(sizeof(char) * (n + 1));
str[0] = '\0';
int strSize = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '(') {
stk[top] = malloc(sizeof(char) * (strSize + 1));
memcpy(stk[top], str, sizeof(char) * (strSize + 1));
top++;
str[0] = '\0';
strSize = 0;
} else if (s[i] == ')') {
reverse(str, strSize);
int len = strlen(stk[top - 1]);
for (int j = strSize; j >= 0; j--) {
str[j + len] = str[j];
}
memcpy(str, stk[top - 1], sizeof(char) * len);
strSize += len;
top--;
} else {
str[strSize++] = s[i];
str[strSize] = '\0';
}
}
return str;
}
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
bool isNumber(char* token) {
return strlen(token) > 1 || ('0' <= token[0] && token[0] <= '9');
}
int evalRPN(char** tokens, int tokensSize) {
int n = tokensSize;
int stk[n], top = 0;
for (int i = 0; i < n; i++) {
char* token = tokens[i];
if (isNumber(token)) {
stk[top++] = atoi(token);
} else {
int num2 = stk[--top];
int num1 = stk[--top];
switch (token[0]) {
case '+':
stk[top++] = num1 + num2;
break;
case '-':
stk[top++] = num1 - num2;
break;
case '*':
stk[top++] = num1 * num2;
break;
case '/':
stk[top++] = num1 / num2;
break;
}
}
}
return stk[top - 1];
}
|