目录
- 1.基础知识
- 2.练习题
- 2.1 斐波那契数列
- 2.2 替换空格
- 2.3 求1+2+…+n
- 2.3.1 方法一:
- 2.3.2 方法二:
- 2.4 在O(1)时间删除链表结点
- 2.5 合并两个排序的链表
- 2.6 左旋转字符串
- 2.7 把字符串转换成整数
- 2.8 反转链表
- 2.9 两个链表的第一个公共结点
- 2.10 删除链表中重复的节点
1.基础知识
1.1 机器是不需要类的,类的出现是为了让人更好理解;
1.2 函数结束{}
后面没有;
,类结束{}
后面有;
1.3 类和结构体差不多,数学组合定义成结构体,大型的、复杂的定义成类
1.4 类不写private,默认是私有的;结构体不写public,默认是共有的
1.5 普通类的模板
#include <iostream>
#include <string>
using namespace std;
class Person
{
private:
int age, height;
double money;
string books[100];
public:
string name;
void say()
{
cout << "I am " << name << endl;
}
int get_age()
{
return age;
}
void add_money(double x)
{
money += x;
}
};
int main()
{
Person c;
c.name = "dwz";
cout << c.get_age() << endl;
c.add_money(10000000000);
return 0;
}
1.6 构造函数的类
普通写法
#include <iostream>
using namespace std;
struct Person
{
int age, height;
double money;
Person() {}
Person(int _age, int _height, double _money)
{
age = _age;
height = _height;
money = _money;
}
};
int main()
{
Person p(18, 180, 1000);
cout << p.age << endl;
cout << p.height << endl;
cout << p.money << endl;
return 0;
}
快速运行版本
#include <iostream>
using namespace std;
struct Person
{
int age, height;
double money;
Person() {}
Person(int _age, int _height, double _money) : age(_age), height(_height), money(_money)
{}
};
int main()
{
Person p(18, 180, 1000);
cout << p.age << endl;
cout << p.height << endl;
cout << p.money << endl;
return 0;
}
1.7 内存可简单认为栈和堆。栈是从上往下生长,堆是从下往上生长。栈里面放局部变量,函数等等,堆里面放全局变量等等。
#include <iostream>
using namespace std;
int main()
{
char s = 'a';
// void * 是无类型指针
// & 是取变量地址的运算符
// (类型)为强制转换类型
// (void *) &变量,就是把变量的地址转换为无类型指针
cout << (void*)&s << endl;
return 0;
}
1.8 数组名也可以做看作指针,数组长度有保存下来
#include <iostream>
using namespace std;
int main()
{
int a[5] = {4, 7, 9, 2};
int* p = &a[1];
int* q = &a[3];
cout << q - p << endl;
return 0;
}
//使用循环输出数组的连续地址
#include <iostream>
using namespace std;
int main()
{
int c[10] = {0,1,2,3,4,5};
for(int i = 0; i < 5; i ++)
cout << (void*) &c[i] << endl;
return 0;
}
1.9 c++中别名,地址同一个
1.10 成员变量调用,普通变量使用点,指针使用箭头。一般头结点指的是第一个节点的地址
1.11 使用结构体构造链表,遍历链表
#include <iostream>
using namespace std;
struct Node
{
int val;
Node* next;
Node(int _val): val(_val), next(NULL) {}
};
int main()
{
Node* p = new Node(1);//加new返回的是地址,不加new是创建了这个类型的数据
Node* q = new Node(2);
Node* o = new Node(3);
Node* head = p;
p->next = q;
q->next = o;
// 链表的遍历方式
for(Node* i = head; i; i = i->next)
cout << i->val << endl;
return 0;
}
1.12 插入节点到首位置,头节点存储的是第一个地址
#include <iostream>
using namespace std;
struct Node
{
int val;
Node* next;
Node (int _val): val(_val), next(NULL){};
};
int main()
{
Node* o = new Node(1);
Node* p = new Node(2);
Node* q = new Node(3);
Node* head = o;
o->next = p;
p->next = q;
for(Node* i = head; i; i = i->next)
cout << i->val << endl;
// 插入节点,置于头指针
Node* x = new Node(4);
x->next = o;
head = x;
for(Node* i = head; i; i = i->next)
cout << i->val << endl;
return 0;
}
1.13 链表的删除节点不是传统意义上的真删除,而是将待删除的节点跳过。步骤是当前节点要找到上一个节点,然后将当前节点的上一节点指向当前节点的下一节点。
#include <iostream>
using namespace std;
struct Node
{
int val;
Node* next;
Node (int _val): val(_val), next(NULL){};
};
int main()
{
Node* o = new Node(1);
Node* p = new Node(2);
Node* q = new Node(3);
Node* head = o;
o->next = p;
p->next = q;
/*
for(Node* i = head; i; i = i->next)
cout << i->val << endl;
*/
// 插入节点,置于头指针
Node* x = new Node(4);
x->next = o;
head = x;
// 删除值为1的节点
head->next = head->next->next;
for(Node* i = head; i; i = i->next)
cout << i->val << endl;
return 0;
}
2.练习题
2.1 斐波那契数列
输入一个整数 n
,求斐波那契数列的第 n
项。
假定从 0
开始,第 0
项为 0
。
数据范围
0≤n≤39
样例
输入整数 n=5
返回 5
class Solution {
public:
int Fibonacci(int n) {
if(n <= 1) return n;
else return Fibonacci(n - 1) + Fibonacci(n - 2);
}
};
2.2 替换空格
请实现一个函数,把字符串中的每个空格替换成"%20"。
数据范围
0≤
输入字符串的长度 ≤1000
。
注意输出字符串的长度可能大于 1000
。
样例
输入:“We are happy.”
输出:“We%20are%20happy.”
class Solution {
public:
string replaceSpaces(string &str) {
string new_str = "";
for(auto i : str)
{
if(i == ' ') new_str += "%20";
else new_str += i;
}
return new_str;
}
};
2.3 求1+2+…+n
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句 (A?B:C)。
数据范围
1≤n≤50000。
样例
输入:10
输出:55
2.3.1 方法一:
思路:根据题意很多方法不能使用, 只能用递归。但是递归在最后一步,需要写if条件停止。为了解决这个问题,使用条件表达式短路。使用且的时候,第一个条件为假的话,直接跳过第二个条件的判断。这个特性就可以作为截止条件。
class Solution {
public:
int getSum(int n) {
int tmp = n;
n > 0 && (tmp += getSum(tmp - 1));
return tmp;
}
};
2.3.2 方法二:
思路:构造一个n*n+1的数组,求数组大小后在右移1个单位,相当于公式 n ∗ ( n + 1 ) / 2 n*(n+1)/2 n∗(n+1)/2
class Solution {
public:
int getSum(int n) {
char a[n][n+1];//注意数据类型char才能符合条件
return sizeof(a)>>1;
}
};
2.4 在O(1)时间删除链表结点
给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。
假设链表一定存在,并且该节点一定不是尾节点。
数据范围
链表长度 [1,500]
。
样例
输入:链表 1->4->6->8
删掉节点:第2个节点即6(头节点为第0个节点)
输出:新链表 1->4->8
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
//*(node) = *(node->next);
}
};
2.5 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
数据范围
链表长度 [0,500]。
样例
输入:1->3->5 , 2->4->5
输出:1->2->3->4->5->5
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode* l1, ListNode* l2) {
auto dummy = new ListNode(-1), tail = dummy;
while(l1 && l2)
{
if(l1->val < l2->val)
{
tail->next = l1;
l1 = l1->next;
}
else
{
tail->next = l2;
l2= l2->next;
}
tail = tail->next;
}
if(l1) tail->next = l1;
if(l2) tail->next = l2;
return dummy->next;
}
};
2.6 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。
请定义一个函数实现字符串左旋转操作的功能。
比如输入字符串"abcdefg"和数字 2
,该函数将返回左旋转 2
位得到的结果"cdefgab"。
注意:
数据保证 n
小于等于输入字符串的长度。
数据范围
输入字符串长度 [0,1000]
。
样例
输入:“abcdefg” , n=2
输出:“cdefgab”
class Solution {
public:
string leftRotateString(string str, int n) {
return str.substr(n) + str.substr(0,n);
}
};
2.7 把字符串转换成整数
请你写一个函数 StrToInt,实现把字符串转换成整数这个功能。
当然,不能使用 atoi 或者其他类似的库函数。
数据范围
输入字符串长度 [0,20]。
样例
输入:“123”
输出:123
注意:
你的函数应满足下列条件:
1.忽略所有行首空格,找到第一个非空格字符,可以是 ‘+/−’ 表示是正数或者负数,紧随其后找到最长的一串连续数字,将其解析成一个整数;
2.整数后可能有任意非数字字符,请将其忽略;如果整数长度为 0,则返回 0;
3.如果整数大于
I
N
T
M
A
X
(
2
31
−
1
)
INT_MAX(2^{31}−1)
INTMAX(231−1),请返回 INT_MAX;如果整数小于INT_MIN(−231
) ,请返回 INT_MIN;
class Solution {
public:
int strToInt(string str) {
string tmp = "";
int len = str.length();
//清洗数据空格
for(int i = 0; i < len; i ++)
{
if(char(str[i]) == ' ') tmp = str.substr(i + 1, len - 1);
}
if(tmp == "") tmp = str;
//清洗数据正负号
int tmp_fuhao = 1;
if(char(tmp[0]) == '+')
{
tmp = tmp.substr(1, tmp.length() - 1);
}
if(char(tmp[0]) == '-')
{
tmp = tmp.substr(1, tmp.length() - 1);
tmp_fuhao = -1;
}
//清洗数据尾数
for(int i = 0; i < tmp.length(); i ++)
{
if(char(tmp[i]) < '0' || char(tmp[i]) > '9')
{
tmp = tmp.substr(0, i);
break;
}
}
//字符串变整数
long long res = 0;
len = tmp.length();
for(int i = 0; i < len; i ++)
{
if(tmp[i] == '+' || tmp[i] == '-') continue;
res = res * 10 + char(tmp[i]) - '0';
if(res > INT_MAX) break;
}
if(tmp_fuhao == -1) res = res * -1;
if(res > INT_MAX) return INT_MAX;
if(res < INT_MIN) return INT_MIN;
return res;
}
};
改进点:
1.string的长度str.size()
2.string的切片是字符
2.8 反转链表
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
思考题:
请同时实现迭代版本和递归版本。
数据范围
链表长度 [0,30]
。
样例
输入:1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL
方法一:迭代版
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
while(!head || !head->next) return head;
auto p = head;
auto q = head->next;
while(q)
{
auto o = q->next;
q->next = p;
p = q;
q = o;
}
head->next = NULL;
return p;
}
};
方法二:递归方法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head;
auto tail = reverseList(head->next);
head->next->next = head;//难点
head->next = NULL;
return tail;
}
};
2.9 两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
当不存在公共节点时,返回空节点。
数据范围
链表长度 [1,2000]
。
保证两个链表不完全相同,即两链表的头结点不相同。
样例
给出两个链表如下所示:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
输出第一个公共节点c1
思路:
两个指针同时遍历不重复的链条部分,走过的长度是相同的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
auto p = headA, q = headB;
while(p != q)
{
if(p) p = p->next;
else p = headB;
if(q) q = q->next;
else q = headA;
}
return p;
}
};
2.10 删除链表中重复的节点
在一个排序的链表中,存在重复的节点,请删除该链表中重复的节点,重复的节点不保留。
数据范围
链表中节点 val 值取值范围 [0,100]
。
链表长度 [0,100]
。
样例1
输入:1->2->3->3->4->4->5
输出:1->2->5
样例2
输入:1->1->1->2->3
输出:2->3
思路:
有可能把头节点删除的题目,需要设置一个虚拟头节点
把相同节点值的看成一段段,p是上一段保留的最后一个节点
q目标是成为下一段的第一个节点,程序运行过程中不一定是
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
//建立虚拟头节点
auto dummy = new ListNode(-1);
dummy->next = head;
auto p = dummy;
while(p->next)
{
auto q = p->next;
//q的更新办法
//q下一个节点不为空并且q的值等于q下一个节点的值,说明发现有重复的
while(q->next && q->next->val == q->val) q = q->next;
//p的更新办法
//如果发现p的下个节点就是q,位置不变,说明没有重复的;否则说明存在重复的
if(p->next == q) p = q;
else p->next = q->next;
}
return dummy->next;
}
};