栈与队列
09. 用两个栈实现队列
题目
思路
用两个栈a1和a2来模拟队列,a1模拟队尾的插入,a2模拟队头的删除 考虑栈a2中可能还有数据,将全部数据移动到a1中,再插入 考虑栈a1中可能还有数据,将全部数据移动到a2中,再删除 考虑队列中没有元素,则返回-1,且用变量保存要删除的数据
代码
class CQueue {
public :
CQueue ( ) {
}
void appendTail ( int value) {
while ( ! a2. empty ( ) )
{
a1. push ( a2. top ( ) ) ;
a2. pop ( ) ;
}
a1. push ( value) ;
}
int deleteHead ( ) {
while ( ! a1. empty ( ) )
{
a2. push ( a1. top ( ) ) ;
a1. pop ( ) ;
}
if ( a2. empty ( ) )
return - 1 ;
else
{
num = a2. top ( ) ;
a2. pop ( ) ;
return num;
}
}
public :
stack< int > a1;
stack< int > a2;
int num = - 1 ;
} ;
30.包含min函数的栈
题目
思路
难点在于如何使min的时间复杂度为O(1) 可以采用一个辅助栈,存储每个区间中的最小值
代码
class MinStack {
public :
MinStack ( ) {
}
void push ( int x) {
a. push ( x) ;
if ( b. empty ( ) )
{
b. push ( x) ;
}
else
{
value = ( x< b. top ( ) ) ? x: b. top ( ) ;
b. push ( value) ;
}
}
void pop ( ) {
a. pop ( ) ;
b. pop ( ) ;
}
int top ( ) {
return a. top ( ) ;
}
int min ( ) {
return b. top ( ) ;
}
public :
stack< int > a, b;
int value;
} ;
链表
06. 从尾到头打印链表
题目
思路
将每个节点的数据压入数组,然后转置数组 vector数组插入:push_back(); 数组的转置: std::reverse(a.begin(), a.end());
代码
class Solution {
public :
vector< int > reversePrint ( ListNode* head) {
vector< int > a;
while ( head)
{
a. push_back ( head-> val) ;
head = head-> next;
}
std:: reverse ( a. begin ( ) , a. end ( ) ) ;
return a;
}
} ;
24. 反转链表
题目
思路
创建哨兵节点,解除头结点的特殊性 用头插法来反转链表 需要考虑链表为空的情况
代码
class Solution {
public :
ListNode* reverseList ( ListNode* head) {
if ( head == NULL )
return NULL ;
ListNode* a = new ListNode ( 1 ) ;
a-> next = head;
while ( head-> next)
{
ListNode* b = head-> next;
head-> next = head-> next-> next;
b-> next = a-> next;
a-> next = b;
}
return a-> next;
}
} ;
35. 复杂链表的复制
题目
回溯+哈希
思路
如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。因为随机指针的存在,其指向的节点可能还没创建,因此需要转化思路 用哈希表记录当前每一个节点对应新节点的情况,拷贝当前节点 创建新节点后,检查【当前节点的后继节点】和【当前节点的随机指针指向的节点】的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们立刻递归地进行创建 为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针返回即可
代码
class Solution {
public :
unordered_map< Node* , Node* > cachedNode;
Node* copyRandomList ( Node* head) {
if ( head == nullptr )
return nullptr ;
if ( ! cachedNode. count ( head) )
{
Node* NewNode = new Node ( head-> val) ;
cachedNode[ head] = NewNode;
NewNode-> next = copyRandomList ( head-> next) ;
NewNode-> random = copyRandomList ( head-> random) ;
}
return cachedNode[ head] ;
}
} ;
迭代+拆分
思路
可以将链表每一个节点拆分为两个相连的节点, 比如A ->B ->C 拆分为 A ->A’ ->B ->B’ ->C ->C‘ 对于任意一个原节点S,其拷贝节点S’就是其后继节点 我们可以直接找到每一个拷贝节点S’的随机指针应当指向的节点
代码
class Solution {
public :
Node* copyRandomList ( Node* head) {
if ( head == nullptr )
return nullptr ;
for ( Node* node = head; node != nullptr ; node = node-> next-> next)
{
Node* nodeNew = new Node ( node-> val) ;
nodeNew-> next = node-> next;
node-> next = nodeNew;
}
for ( Node* node = head; node != nullptr ; node = node-> next-> next)
{
Node* nodeNew = node-> next;
nodeNew-> random = ( node-> random != nullptr ) ? node-> random-> next: nullptr ;
}
Node* headnew = head-> next;
for ( Node* node = head; node != nullptr ; node = node-> next)
{
Node* nodeNew = node-> next;
node-> next = node-> next-> next;
nodeNew-> next = ( node-> next != nullptr ) ? node-> next-> next: nullptr ;
}
return headnew;
}
} ;
字符串
05. 替换空格
题目
代码
class Solution {
public :
string replaceSpace ( string s) {
string array;
for ( char & ch: s)
{
if ( ch == ' ' )
array += "%20" ;
else
array += ch;
}
return array;
}
} ;
58. ΙΙ. 左旋转字符串
题目
代码
class Solution {
public :
string reverseLeftWords ( string s, int n) {
string a;
for ( int i= n; i< s. size ( ) ; i++ )
{
a += s[ i] ;
}
for ( int i= 0 ; i< n; i++ )
{
a += s[ i] ;
}
return a;
}
} ;
查找算法
03. 数组中重复的数字
题目
代码
class Solution {
public :
int findRepeatNumber ( vector< int > & nums) {
for ( auto & ch: nums)
{
if ( m[ ch] == 1 )
return ch;
m[ ch] ++ ;
}
return - 1 ;
}
public :
unordered_map< int , int > m;
} ;
官方题解
class Solution {
public :
int findRepeatNumber ( vector< int > & nums) {
unordered_map< int , bool > array;
for ( int i= 0 ; i< nums. size ( ) ; i++ )
{
if ( array[ nums[ i] ] )
{
return nums[ i] ;
}
array[ nums[ i] ] = true ;
}
return - 1 ;
}
} ;
53. Ι. 在排序数组中查找数字
题目
暴力题解
思路
遍历一次,找到第一个目标值的位置 从这个位置往后开始计数,得到目标值出现的次数
代码
class Solution {
public :
int search ( vector< int > & nums, int target) {
int count = 0 ;
for ( auto & sh: nums)
{
if ( sh == target)
count++ ;
}
return count;
}
} ;
二分查找
思路
用二分查找算法得到目标值第一次出现的位置和大于目标值的位置 因为是有序数组,相减得到重复的次数
代码
class Solution {
public :
int binarySearch ( vector< int > & nums, int target, bool lower)
{
int left = 0 , right = nums. size ( ) - 1 , ans = nums. size ( ) ;
while ( left <= right)
{
int mid = left + ( left - right) / 2 ;
if ( nums[ mid] > target || ( lower && nums[ mid] >= target) )
{
right = mid- 1 ;
ans = mid;
}
else
{
left = mid+ 1 ;
}
}
return ans;
}
int search ( vector< int > & nums, int target) {
int leftIdx = binarySearch ( nums, target, true ) ;
int rightIdx = binarySearch ( nums, target, false ) - 1 ;
if ( leftIdx <= rightIdx && rightIdx < nums. size ( ) && nums[ leftIdx] == target && nums[ rightIdx] == target)
{
return rightIdx - leftIdx + 1 ;
}
return 0 ;
}
} ;
53. ΙΙ. 0~n-1中缺失的数字
题目
思路
在有序数组中查找数字,可以用二分查找 因为是从0开始连续,考虑条件为是否等于所在位置 左子数组:nums【i】 == i; 右子数组:nums【i】!= i; 跳出时,变量left和right分别指向“右子数组的首位元素”和“左子数组的末位元素”, 因此返回left即可
代码
class Solution {
public :
int missingNumber ( vector< int > & nums) {
int left = 0 , right = nums. size ( ) - 1 ;
while ( left <= right)
{
int mid = left + ( right - left) / 2 ;
if ( nums[ mid] == mid)
{
left = mid + 1 ;
} else {
right = mid - 1 ;
}
}
return left;
}
} ;
04. 二维数组中的查找
题目
思路
线性查找 从二维数组的右上角开始查找 如果当前元素等于目标值,则返回true。如果当前元素大于目标值,则移动左边一列。如果当前元素小于目标值,则移到下一行
代码
class Solution {
public :
bool findNumberIn2DArray ( vector< vector< int >> & matrix, int target) {
if ( matrix. size ( ) == 0 || matrix[ 0 ] . size ( ) == 0 )
return false ;
int rows = matrix. size ( ) , columns = matrix[ 0 ] . size ( ) ;
int row = 0 , column = columns- 1 ;
while ( row < rows && column >= 0 )
{
if ( matrix[ row] [ column] == target)
return true ;
else if ( matrix[ row] [ column] > target)
{
column-- ;
} else {
row++ ;
}
}
return false ;
}
} ;
11. 旋转数组的最小数字
题目
思路
旋转数组后,数组分为两部分:左排序递增数组和右排序递增数组,且左边数组的值大于等于右边数组的值 我们的目标是寻找右排序数组的最小元素x,设m=( i + j ) / 2 为每次二分算法的中点 当 nums[m] > nums[j] 时, m一定在左排序数组中,即旋转点x一定在【m+1,j】闭区间内,因此执行i=m+1 当 nums[m] < nums[j] 时, m一定在右排序数组中,即旋转点x一定在【i, m】闭区间内, 因此执行j = m 当 nums[m] = nums[j] 时, 无法判断m在哪个排序数组中,执行j = j-1 缩小判断范围 返回值:当i=j时跳出二分循环,并返回旋转点的值nums[i]
代码
class Solution {
public :
int minArray ( vector< int > & numbers) {
int left = 0 , right = numbers. size ( ) - 1 ;
while ( left < right)
{
int mid = left + ( right - left) / 2 ;
if ( numbers[ mid] < numbers[ right] )
{
right = mid;
}
else if ( numbers[ mid] > numbers[ right] )
{
left = mid+ 1 ;
}
else
{
right -= 1 ;
}
}
return numbers[ left] ;
}
} ;
50. 第一次只出现一次的字符
题目
思路
代码
class Solution {
public :
char firstUniqChar ( string s) {
char a = ' ' ;
unordered_map< char , int > map;
if ( s. size ( ) == 0 )
return a;
for ( auto & ch: s)
{
map[ ch] ++ ;
}
for ( auto & ch: s)
{
if ( map[ ch] == 1 )
return ch;
}
return a;
}
} ;
参考文章