剑指offer 面试题 3:数组中重复的数字
题目:
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:
2 或 3
解法一:排序之后循环遍历
排序之后,从头到尾循环i和i+1位置的元素进行比较,如果相等就输出这个重复的数字,时间复杂度是排序算法的时间复杂度加上循环的复杂度O(n)
void FindRepeatNumber(vector<int>&v)
{
sort(v.begin(), v.end());//C++内置的排序函数 根据数据的不同有不同的算法,底层多是优化之后的快速排序,当然还有其他的排序算法,因数据而易;
int i = 0;
for (i = 1; i < v.size(); i++)
{
if (v[i - 1] != v[i])//这里如果是v[i]和v[i+1]比较当为最后一个元素就会访问越界
{
i++;
}
if (v[i - 1] == v[i])
{
cout << v[i];
}
}
}
解法2:使用哈希表
解法3:扫描下标与交换元素
从题目中我们知道数组的长度为 n 数组中的元素个数为 0~n-1 也就是说
例如:数组的长度为 7 所有的元素在 0~6 范围内,理想不重复的情况下是 0,1,2,3,4,5,6
这时候0号下标对应 元素为 0 1 号下标对应 1号依次类推
因此,我们只需要遍历数组,如果下标位置不等于该下标的元素就将该数字交换到相应的下标位置即可,如果相等就继续扫描下一个下标位置的元素
例如:测试用例:[2,3,1,0,2,5,3]
第一次交换:[1,3,2,0,2,5,3] [3,1,2,0,2,5,3] [0,1,2,3,2,5,3]
判断 i==v[i] i++ 当 i=4 此时 v[v[4]]=v[4] 这时便找到了第一个重复的数字
一个元素最多只需要交换两次就可以将其放到其相应的位置,时间复杂度为 O(N)空间复杂度为O(1)
代码:
void FindRepeatNumber2(vector<int>&v)
{
int i = 0;
for (i = 0; i < v.size(); i++)
{
while (i != v[i])
{
if (v[i]==v[v[i]])
{
cout << v[i];
return;
}
int temp = v[v[i]];
v[v[i]] = v[i];
v[i] = temp;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}
}
交换过程与结果为:
测试用例
[5,3,4,2,5,6,0]
leetcode ACE代码为:
class Solution {
public:
int findRepeatNumber(vector<int>& nums)
{
int i = 0;
for (i = 0; i < nums.size(); i++)
{
while (i != nums[i])
{
if (nums[i]==nums[nums[i]])//当产生了重复的数字时返回
{
return nums[i];
}
int temp = nums[nums[i]];//交换数据
nums[nums[i]] = nums[i];
nums[i] = temp;
}
}
return -1;
}
};
剑指 Offer 04. 二维数组中的查找
题目:
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
1 | 4 | 7 | 1 |
2 | 5 | 8 | 12 |
3 | 6 | 9 | 16 |
10 | 13 | 14 | 17 |
18 | 21 | 23 | 26 |
给定 target = 5,返回 true。
给定 target = 20,返回 false
解题思路:按照数组的顺序右上角(或者左下角)是第一行最大第一列最小(左下角的就是第一列最大,最后一行最小的),只需要将要查找的数字与 右上角的 如示例的 15 比较(若相等直接返回true) 20 >15 则数字在的范围是第二行 行数加加;再于19 比较 ,若是小,则列数减减;依次循环 ,若找到了最后一行或者最后一列还是没有找到则返回false,若在找的过程中相等则返回true即可
通过代码:
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if(matrix.empty())//注意数组为空的情况
{
return false;
}
int i=0;
int j =matrix[0].size()-1;//最后一列的列数
while (i <matrix.size() && j>=0)
{
if (target == matrix[i][j])//相等表示找到了 直接返回false
{
return true;
}
if (target > matrix[i][j])//比该数字大就向下找 行数加加
{
++i;
if (i == matrix.size())//直到最后一行还没找到就返回 否则将会越界
{
return false;
}
}
if (target < matrix[i][j])//直到最后一列还没有找到就返回
{
--j;
if (j < 0)
{
return false;
}
}
}
return false;
}
};
本地完整测试代码:
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if(matrix.empty())//注意数组为空的情况
{
return false;
}
int i=0;
int j =matrix[0].size()-1;//最后一列的列数
while (i <matrix.size() && j>=0)
{
if (target == matrix[i][j])//相等表示找到了 直接返回false
{
return true;
}
if (target > matrix[i][j])//比该数字大就向下找 行数加加
{
++i;
if (i == matrix.size())//直到最后一行还没找到就返回 否则将会越界
{
return false;
}
}
if (target < matrix[i][j])//直到最后一列还没有找到就返回
{
--j;
if (j < 0)
{
return false;
}
}
}
return false;
}
};
int main()
{
vector<vector<int>> arr;
int i, j;
int n, m;
cin >> n;
cin >> m;
for (i = 0; i < n; i++)//初始化数据
{
vector<int> v;
for (j = 0; j < m; j++)
{
int w;
cin >> w;
v.push_back(w);
}
arr.push_back(v);
}
Solution a;
if (a.findNumberIn2DArray(arr, 8))//如果找到了返回 true
{
cout << "true";
}
else//否则返回false
{
cout << "false";
}
return 0;
}
剑指 Offer 05. 替换空格
题目:请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:
s = “We are happy.”
输出:
“We%20are%20happy.”
1、 解题思路:直接遍历,不是空格的字符插入到一个新的string 对象中,遇到空格插入%20最后将该字符串返回即可
leetcode ace 代码:
class Solution {
public:
string replaceSpace(string& s) {
string s1;
string s2("%20");
for (int i = 0; i < s.size(); i++)
{
if (s[i] == ' ')
{
s1 += s2;
}
if (s[i] != ' ')
{
s1.push_back(s[i]);
}
}
return s1;
}
};
剑指 Offer 06. 从尾到头打印链表
题目:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:
head = [1,3,2]
输出:
[2,3,1]
单链表由于其只能访问结点的下一个结点,导致在逆序打印的时候非常的麻烦,这里有两种方法:
一、栈 非递归
由于栈的后进先出的特点我们只需要将单链表中的元素按照顺序压入栈中,然后再按顺序出栈就可以了。
代码如下:
void ReversePrintList(List *l)//逆向打印链表中的内容 利用栈的特性 后进先出的特点 进行逆序遍历 时间复杂度是 O(n)
{
stack<int> s;
ListNode* ptr=l->head;
while(ptr!=NULL)
{
s.push(ptr->value);//将节点顺序压栈
ptr=ptr->next;
}
while(!s.empty())
{
cout<<s.top()<<" ";
s.pop();//依次出栈,这就是一个逆序的情况 例如进栈为 1 2 3 4 则出栈顺序为 4 3 2 1 实现了逆序打印链表 从尾到头打印链链表
}
cout<<endl;
}
二、递归遍历
其实从尾到头打印,我们可以递归的找链表结点的下一个,直到最后一个结点然后打印,然后再打印最后一个结点的前一个结点,这样一直打印到第一个结点完成了遍历。
void ReversePrintList2(ListNode *head)//递归实现单链表的反向打印,递归打印下一个节点的值如果这个节点的next不为空的情况下 当循环到最后一个节点的时候开始返回并打印节点的值
{
ListNode *ptr=head;
if(ptr==NULL)
{
return;
}
else//但是有一个缺点是当这个链表的长度非常的长的时候 ,调用的函数会比较深 导致最后的栈溢出
{
if(ptr->next!=NULL)
{
ReversePrintList2(ptr->next);
}
cout<<ptr->value<<" ";
}
}
完整代码如下:
#include<iostream>
#include<stdlib.h>
#include<stack>
#include<time.h>
using namespace std;
typedef struct ListNode//定义结点结构体类型
{
int value;
ListNode* next;
}ListNode;
typedef struct List
{
ListNode* head = NULL;
}List;
void AddNode(List *l, int Value)
{
ListNode* newnode = new ListNode();
newnode->value = Value;
newnode->next = NULL;
if (l->head == NULL)
{
l->head = newnode;//第一次插入节点的时候 没有节点 将第一个节点置为头结点
}
else
{
ListNode *ptr = l->head;
while (ptr->next != NULL)//找到尾结点
{
ptr = ptr->next;
}
ptr->next = newnode;//将新节点链上尾结点的后面
}
}
void PrintList(List *l)
{
if (l->head == NULL)
{
return;
}
else
{
ListNode* ptr = l->head;
while (ptr->next != NULL)
{
cout << ptr->value << " ";
ptr = ptr->next;
}
cout << ptr->value;
cout << endl;
}
}
void ReversePrintList(List *l)//逆向打印链表中的内容 利用栈的特性 后进先出的特点 进行逆序遍历 时间复杂度是 O(n)
{
stack<int> s;
ListNode* ptr = l->head;
while (ptr != NULL)
{
s.push(ptr->value);//将节点顺序压栈
ptr = ptr->next;
}
while (!s.empty())
{
cout << s.top() << " ";
s.pop();//依次出栈,这就是一个逆序的情况 例如进栈为 1 2 3 4 则出栈顺序为 4 3 2 1 实现了逆序打印链表 从尾到头打印链链表
}
cout << endl;
}
void ReversePrintList2(ListNode *head)//递归实现单链表的反向打印,递归打印下一个节点的值如果这个节点的next不为空的情况下 当循环到最后一个节点的时候开始返回并打印节点的值
{
ListNode *ptr = head;
if (ptr == NULL)
{
return;
}
else//但是有一个缺点是当这个链表的长度非常的长的时候 复杂度会比较的高,调用的函数会比较深 导致最后的栈溢出
{
if (ptr->next != NULL)
{
ReversePrintList2(ptr->next);
}
cout << ptr->value << " ";
}
}
int main()
{
List l;
int i = 0;
AddNode(&l, 1);
AddNode(&l, 2);
AddNode(&l, 3);
AddNode(&l, 6);
PrintList(&l);
cout << "使用栈进行逆向打印: " ;
ReversePrintList(&l);
cout << "递归逆向打印: " ;
ReversePrintList2(l.head);
return 0;
}
打印结果为:
leetcode ACE代码如下:
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
ListNode* ptr=head;
stack<int> s;
if(ptr!=nullptr)
{
while(ptr!=nullptr)
{
s.push(ptr->val);
ptr=ptr->next;
}
}
vector<int> v;
while(!s.empty())
{
v.push_back(s.top());
s.pop();
}
return v;
}
};