1. 为什么基类的析构函数要声明成虚函数?
原因:
在公有继承中,基类的虚构函数如果不声明成为虚函数,那么销毁派生类时有可能造成资源泄漏
- class Base{ public:
- Base( ) { };
- ~Base( ) { };
- };
- class Derived :public Base{
- public:
- Derived( ) { };
- ~Derived( ){ };
- };
- void main( )
- {
- Base *p;
- p = new Derived;
- delete p;
- }
这种情况会发生销毁不完全的情况,因为delete p调用的是声明类型(即基类)的析构函数,所以只能销毁基类对象而无法销毁派生类对象。
修改:
- class Base{ public:
- Base( ) { };
- virtual ~Base( ) { };
- };
- class Derived :public Base{
- public:
- Derived( ) { };
- ~Derived( ){ };
- };
- void main( )
- {
- Base *p;
- p = new Derived;
- delete p;
- }
当基类的析构函数声明为虚函数,那么派生类的析构函数也是虚函数,此时调用delete p时发生动态绑定,运行时会根据实际类型调用该对象的虚函数。
当然,并不是要把所有类的析构函数都写成虚函数。只有当一个类是基类(即希望被继承)的时候才需要声明成虚函数,因为虚函数的作用是实现多态,而多态是建立在继承的基础上。单一类不能把析构函数写成虚函数,因为会产生额外的开销,比如虚表的创建和虚指针的定义。
2 二分查找算法
转自:https://blog.youkuaiyun.com/silence723/article/details/52043670
二分查找算法是在有序数组中用到的较为频繁的一种算法,在未接触二分查找算法时,最通用的一种做法是,对数组进行遍历,跟每个元素进行比较,其时间为O(n).但二分查找算法则更优,因为其查找时间为O(lgn),譬如数组{1, 2, 3, 4, 5, 6, 7, 8, 9},查找元素6,用二分查找的算法执行的话,其顺序为:
1.第一步查找中间元素,即5,由于5<6,则6必然在5之后的数组元素中,那么就在{6, 7, 8, 9}中查找,
2.寻找{6, 7, 8, 9}的中位数,为7,7>6,则6应该在7左边的数组元素中,那么只剩下6,即找到了。
二分查找算法就是不断将数组进行对半分割,每次拿中间元素和goal进行比较。
- #include<iostream>
- using namespace std;
- int binary_search(int arr[], int n, int key)
- {
- int left = 0; //数组的首位置,即arr[0]处
- int right = n - 1;//数组的最后一个位置,即arr[n-1],数组大小为n
- //循环条件一定要注意
- while (left <= right)
- {
- int mid = left + ((right - left) >> 1);//此处的mid的计算一定要放在while循环内部,否则mid无法正确更新;并且此处用移位代替除以2可以提高效率,而且可以防止溢出。
- if (arr[mid] > key)//数组中间的位置得数大于要查找的数,那么我们就在中间数的左区间找
- {
- right = mid - 1;
- }
- else if (arr[mid] < key)//数组中间的位置得数小于要查找的数,那么我们就在中间数的右区间找
- {
- left = mid + 1;
- }
- else
- {
- return mid;//中间数刚好是要查找的数字。
- }
- }
- //执行流如果走到此处说明没有找到要查找的数字。
- return -1;
- }
- 测试用例如下所示:
- void Test()
- {
- int arr[5] = { 1, 3, 5, 9, 10 };
- int ret1 = 0,ret2 = 0,ret3 = 0;
- ret1 = binary_search(arr, 5, 9);
- cout << ret1 << endl;
- ret2 = binary_search(arr, 5, 5);
- cout << ret2 << endl;
- ret3 = binary_search(arr, 5, 2);
- cout << ret3 << endl;
- }
此外:大部分人喜欢在最开始就判断arr[mid]与key相等,但是这样是不明智的,因为相等情况在大多状况下都是少数,写在开始的话,每次循环都需要判断一次相等,浪费时间,效率太低。
3.链表相关问题
转自:https://blog.youkuaiyun.com/hyqsong/article/details/49429859
反转链表
输入一个链表,反转链表后,输出链表的所有元素。
据找工作的师兄说,反转单链表基本各个公司面试都会有,整理出一些写的比较好的code,供我等小白们学习。
方法一
- 常规思路,简洁,清晰,我觉得写得蛮好的。
思路就是: 从原链表的头部一个一个取节点并插入到新链表的头部
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* newh = NULL;
for(ListNode* p = pHead; p; )//p为工作指针
{
ListNode* tmp = p -> next;//temp保存下一个结点
p -> next = newh;
newh = p;
p = tmp;
}
return newh;
}
};
- 这是我一开始写得,留着对比用,衬托,哈哈哈
思路一样,但是感觉没第一个写得简洁,但是我的绝对最好理解哈
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == NULL)
return pHead;
ListNode *res,*cur,*next;
res = new ListNode(-1);
cur = pHead;
next = cur->next;
while(cur != NULL)
{
ListNode *first = res->next;
cur->next = first;
res->next = cur;
cur = next;
next = next->next;
}
return res->next;
}
};
方法二
思路:每次都将原第一个结点之后的那个结点放在新的表头后面。
比如1,2,3,4,5
第一次:把第一个结点1后边的结点2放到新表头后面,变成2,1,3,4,5
第二次:把第一个结点1后边的结点3放到新表头后面,变成3,2,1,4,5
……
直到: 第一个结点1,后边没有结点为止。
代码如下:
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead == NULL)
return pHead;
ListNode *res,*first,*temp;
res = new ListNode(-1);
res->next = pHead;
first = res->next; //first 始终为第一个结点,不断后移
while(first->next!=NULL) //temp为待前差的
{
temp = first->next;
first->next = temp->next;
temp->next = res->next;
res->next = temp;
}
return res->next;
}
};
方法三
第三种方法跟第二种方法差不多,第二种方法是将后面的结点向前移动到头结点的后面,第三种方法是将前面的结点移动到原来的最后一个结点的后面,思路跟第二种方法差不多,就不贴代码了。
3.2 删除单向链表中的某一个节点
原文链接:http://blog.youkuaiyun.com/huahuahailang/article/details/8762785
已知一个单向链表的表头head,写出一个删除某一个节点的算法,要求先找到此节点,然后删除。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273#include <iostream>
using
namespace
std;
typedef
struct
node
{
int
number;
struct
node *next;
}Node;
Node *Delete(Node *head,
int
key){
Node *node1 = head;
Node *node2 = NULL;
if
(head == NULL){
return
NULL;
}
else
{
if
(node1->number == key){
head = head->next;
free
(node1);
return
head;
}
else
{
while
(node1 != NULL){
node2 = node1;
node2 = node1 -> next;
if
(node2 -> number == key){
node1 -> next = node2 -> next;
free
(node2);
break
;
}
node1 = node1 -> next;
}
return
head;
}
}
}
int
main(){
Node *head = (Node*)
malloc
(
sizeof
(Node));
Node *p, *q, *q1;
int
key;
p = (Node*)
malloc
(
sizeof
(Node));
q1 = q = head;
int
i;
for
(
int
i = 1; i < 10; i++){
p -> number = i;
head -> next = p;
head = p;
p = (Node*)
malloc
(
sizeof
(Node));
}
head -> next = NULL;
cout <<
"原链表数据:"
<< endl;
q1 = q1 -> next;
while
(q1 != NULL){
cout << q1 -> number <<
" "
;
q1 = q1 -> next;
}
cout << endl;
cout <<
"输入要删除的数据:"
;
cin >> key;
p = Delete(q -> next, key);
cout <<
"删除一个"
<< key <<
"之后的链表数据:"
<< endl;
while
(p != NULL){
cout << p -> number <<
" "
;
p = p ->next;
}
cout << endl;
free
(p);
free
(head);
return
0;
}
程序运行结果:
原链表数据:
1 2 3 4 5 6 7 8 9
输入要删除的数据:5
删除一个5之后的链表数据:
1 2 3 4 6 7 8 9