求单链表的最大值与原地逆转_GitHub - bayec/c_cpp_khbd: C/C++面试宝典

C/C++面试宝典

目录

1 常识概念题

1.1 C/C++基础

1.1.1 面向对象的三大特点

封装

把数据和成员函数封装到一起形成一个类,隐藏类内部的实现细节,把不想告诉别人的东西隐藏起来,可以告诉别人的东西公开,增加安全性。

继承

从已有的类中派生出新的类称为子类,子类继承父类的成员,并能根据自己的需求扩展出新的行为,提高了代码的复用性。

多态

同一种事物所表现出的多种形态,简单地说是“一个接头,多种实现”。

有两种类型的多态:

编译时的多态,编译时的多态性是通过重载来实现的;

运行时的多态,运行时的多态性是通过虚函数实现的,比如使用父类的指针调用子类的方法。

1.1.2 结构体和类的区别

struct默认访问控制符是public,而class是private。

1.1.3 空类占多少个字节

1个字节,没有数据成员的类叫空类。

1.1.4 引用占用内存空间吗

不占。

1.1.5 公有继承和私有继承的区别

继承方式

父类的public成员

父类的protected成员

父类的private成员

public继承

还是public成员

变为protected成员

不可见

protected继承

变成protected成员

还是protected成员

不可见

private继承

变为private成员

变成private成员

不可见

1.1.6 虚析构函数的作用

父类的指针指向子类的对象,销毁时普通析构函数仅调用父类的析构函数,可能造成内存泄漏。

1.1.7 C++强制类型转换有哪些

static_cast:类似类型转换,比如double转int。

dynamic_cast:父类强制转成子类。

const_cast:const转成非const,反之亦可。

reinterpret_cast:非常危险的转换,整型直接转指针。

1.1.8 重写(覆盖)和隐藏的区别

重写是子类重写父类的虚函数,隐藏是子类和父类的普通函数名相同。

1.1.9 深拷贝和浅拷贝

浅拷贝是将对象中的数据直接拷贝到另一个对象中,深拷贝会将对象中指针所指向的内存也一起拷贝。如果对象中有指针,浅拷贝会导致新旧对象中的指针指向同一块内存,可能造成delete两次出错。

1.1.10 标准模板库的vector是否能当作栈

可以。

1.1.11 指针和引用的区别

指针是存放变量地址的一个变量,而引用只是某块内存的别名;

引用在定义的时候必须初始化,之后不能改变,而指针可以不初始化,也可以改变;

有空指针,但是没有空引用;

有指针数组,但是没有引用数组;有指针的指针,但是没有引用的引用;

sizeof指针等于4(32位)或者8(64位机器),sizeof引用等于所指变量的大小,但是当引用作为成员时,占用空间和指针一样。

1.1.12 C++空类默认成员函数

class A

{

public:

A();//默认构造函数

A(const A&);//拷贝构造函数

~A();//析构函数

A& operator=(const A&);//默认赋值运算符

A& operator&();//默认取地址运算符

const A* operator&() const;//取地址运算符const

};

1.1.13 内联函数和宏定义的区别

宏定义在预编译阶段进行代码替换,而内联函数在编译阶段插入代码;

宏定义没有类型检查,而内联函数有类型检查。

1.1.14 构造函数可以是虚函数吗

不可以,首先编译不通过,另外虚函数表指针是在构造函数之后创建的,所以构造函数不可能是虚函数。

1.2 数据库

1.2.1 关系数据库和文件数据库的区别

关系数据库特征是数据的结构化,而文件数据库是数据的非结构化;

关系数据库中用户看到的逻辑结构是二维表,而文件数据库基本元素是文件;

文件数据库可以实现多媒体文件管理,支持C/S。

1.2.2 内连接和外连接区别

内连接也称为自然连接,显示两个表相匹配的行,舍弃不匹配的数据;外连接又分为左外连接、右外连接、全连接,只限制其中一个表的行,而不限制另一个表的行。

例如下面有两个表,学生表和课程表:

学号

姓名

0001

张三

0002

李四

0003

王五

学号

课程

0001

语文

0002

数学

0003

英语

0004

计算机

两个表内连接结果如下:

学号

姓名

课程

0001

张三

语文

0002

李四

数学

0003

王五

英语

两个表左外连接结果如下:

学号

姓名

课程

0001

张三

语文

0002

李四

数学

0003

王五

英语

0004

计算机

1.2.3 什么是事务

事务是一个单独的执行单元,要么成功,要么失败,执行成功数据便会提交,失败则回滚。

事务具有四个特性,称为ACID:原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)。

1.2.4 什么是主键?什么是外键?

主键是一个或多个字段,是表中记录的唯一标识符,主键不能为空,一个表中只能有一个主键。

外键表示的是两个表之间的联系,如果表A和表B都有字段X,X是表A的主键,那么X称为B的外键,外键可以为空,一个表可以有多个外键。

1.2.5 什么是视图

视图是一张虚表,从基本表中选取出来的数据组成的一张表。视图的作用如下:

简化数据查询语句

提高数据的安全性

逻辑独立性

1.3 网络知识

1.3.1 TCP三次握手和四次挥手

title: 三次握手

participant 客户端 as c

participant 服务器 as s

Note right of s: 大写单词表示标志位,小写单词表示序号

c->s: SYN=1 seq=X

s->c: SYN=1 ACK=1 ack=X+1 seq=Y

c->s: ACK=1 ack=Y+1 seq=X+1

title: 四次挥手

participant 客户端 as c

participant 服务器 as s

c->s: FIN=1 seq=X

s->c: ACK=1 seq=Y ack=X+1

s->c: FIN=1 ACK=1 seq=Z ack=X+1

c->s: ACK=1 seq=X+1 ack=Z+1

1.3.2 socket编程流程

graph TB

服务器-->调用socket函数创建套接字

调用socket函数创建套接字-->调用bind函数绑定ip地址和端口号

调用bind函数绑定ip地址和端口号-->调用listen函数开始侦听

调用listen函数开始侦听-->调用accept函数接收连接

调用accept函数接收连接-->调用recv/send开始读写数据

调用recv/send开始读写数据-->调用close函数关闭连接

客户端-->同样调用socket函数创建套接字

同样调用socket函数创建套接字-->调用connect函数连接服务器

调用connect函数连接服务器-->调用recv/send或者read/write开始读写数据

调用recv/send或者read/write开始读写数据-->调用close函数关闭套接字

1.4 操作系统

1.4.1 进程和线程的区别

进程是资源分配的基本单位,线程是CPU调度的基本单位;

一个进程可以包含多个线程,但是一个线程只能属于一个进程;

每个进程有自己独立的内存空间,创建和进程间切换开销大,而同属一个进程的所有线程共享进程的内存空间,所以创建和线程间切换开销小;

多进程比多线程更健壮,多线程程序只要有一个线程挂了,那么整个进程就挂了;

进程间通信麻烦(消息队列、管道、共享内存、信号量),线程间通信方便,使用全局变量就行了。

1.4.2 死锁形成的原因

互斥锁使用不当,一个线程得到锁而不释放就会造成死锁;

系统资源不足,多个进程共享同一块资源,这些资源同一时刻只能被一个进程使用,此时就会出现多个进程抢夺资源造成死锁;

进程运行顺序不当,比如两个进程P1和P2,它俩必须同时拥有资源R1和R2时才能正常运行,如果P1占着R1,P2占着R2,此时就会死锁。

2 编程题

2.1 数组相关操作

2.1.1 用递归算法实现数组求和

int sum(int* a, int n)

{

return n == 0 ? 0 : sum(a, n - 1) + a[n - 1];

}

2.1.2 用递归算法判断数组是否是递增

bool isIncrease(int* a, int n)

{

if (n == 1)

return true;

return (a[n - 1] >= a[n - 2]) && isIncrease(a, n - 1);

}

2.1.3 用递归算法实现二分查找

int halfSearch(int* a, int key, int start, int end)

{

printf("%d, %d\n", start, end);

if (a == NULL || start > end)

return -1;

int mid = start + (end - start) / 2;

if (a[mid] == key)

return mid;

else if(a[mid] > key)

return halfSearch(a, key, start, mid - 1);

else

return halfSearch(a, key, mid + 1, end);

}

2.1.4 找出数组中重复次数最多的数字

方法一:

int mostNum(int* a, int size)

{

int count[100] = { 0 };

int tmp = 0;

for (int i = 0; i < size; i++)

{

count[a[i]]++;

}

tmp = count[0];

for (int i = 1; i < 100; i++)

{

if (tmp < count[i])

{

tmp = count[i];

}

}

return tmp;

}

方法二(C++):

int mostNum(int* a, int size)

{

int tmp = 0;

map m;

for (int i = 0; i < size; i++)

{

if (++m[a[i]] >= m[tmp])

{

tmp = a[i];

}

}

return tmp;

}

2.1.5 删除数组中重复的元素

int deleteRepeat(int* a, int size)

{

if (a == NULL || size <= 0)

return false;

int j = 0;

for (int i = 1; i < size; i++)

{

if (a[j] != a[i])

{

a[j + 1] = a[i];

j++;

}

}

return j+1;

}

2.1.6 找出数组中第二大的数

int secondMax(int* a, int size)

{

int max = 0;

int sec = 0;

for (int i = 0; i < size; i++)

{

if (max < a[i])

{

sec = max;

max = a[i];

}

else if(max == a[i])

{

continue;

}

else

{

if (sec < a[i])

sec = a[i];

}

}

return sec;

}

2.1.7 数组左移和右移

//左移

void lmove(int* a, int len, int offset)

{

int b[10] = { 0 };

//backup

memmove(b, a, sizeof(a[0]) * len);

for (int i = 0; i < len; i++)

{

if (i < len-offset)

a[i] = b[i+offset];

else

a[i] = b[i+offset-len];

}

}

//右移

void rmove(int* a, int len, int offset)

{

int b[10] = { 0 };

//backup

memmove(b, a, sizeof(a[0]) * len);

for (int i = 0; i < len; i++)

{

if (i < offset)

a[i] = b[len + i - offset];

else

a[i] = b[i - offset];

}

}

2.1.8 数组中有一个元素只出现一次,其它元素出现两次

int findOnlyOne(int* a, int len)

{

int ret = a[0];

for (int i = 1; i < len; i++)

{

ret ^= a[i];

}

return ret;

}

2.1.9 一次遍历寻找链表的倒数某个节点

/* 一次遍历寻找链表的倒数某个节点 */

List *backward(List *phead, int pos)

{

List *p1, *p2;

p1 = p2 = phead;

for(int i = 0; i < pos; i++)

{

p1 = p1->next;

}

while(p1 != NULL)

{

p1 = p1->next;

p2 = p2->next;

}

return p2;

}

2.2 链表相关操作

2.2.1 单链表反转

List* reverse_list(List *list)

{

if(list == NULL || list->next == NULL)

{

return list;

}

List* pPrev = list;

List* pCur = list->next;

List* pNext = NULL;

list->next = NULL;

while(pCur)

{

pNext = pCur->next;

pCur->next = pPrev;

pPrev = pCur;

pCur = pNext;

}

list = pPrev;

return list;

}

2.2.2 逆序输出

/* 逆序输出 */

void reverse_print(List *list)

{

if(list != NULL)

{

reverse_print(list->next);

printf("name = %s, age = %d\n", list->data.name, list->data.age);

}

}

2.3 字符串相关操作

详见string目录下面的代码。

2.3.1 统计一行字符串有多少个单词

详见string/word_in_string.c

int word_in_string(const char *str)

{

int count = 0;

int isWord = 0;

int i = 0;

char c = 0;

for(i = 0; (c = str[i]) != '\0'; i++)

{

if(c == ' ')

{

isWord = 0;

}

else if(isWord == 0)

{

isWord = 1;

count++;

}

}

return count;

}

2.3.2 字符串逆序

详见string/reverse_string.c,要求不允许创建新的字符数组, 直接原地逆转。

方法一

char *reverse_string1(char *str)

{

char *p = str;

char *q = str;

while(*q)

{

q++;

}

q--;

while(q > p)

{

char tmp = *p;

*p++ = *q;

*q-- = tmp;

}

return str;

}

方法二

char *reverse_string2(char *str, int left, int right)

{

while(left < right)

{

char tmp = str[left];

str[left++] = str[right];

str[right--] = tmp;

}

return str;

}

2.3.3 实现strlen

详见string/mystrlen.c,要求不能定义局部变量。

int my_strlen(const char *str)

{

return *str ? my_strlen(str + 1) + 1: 0;

}

2.3.4 找出字符串中第一个只出现一次的字符

详见string/unique_string.cpp。

char getUniqueChar(char *str)

{

map m;

for(char *p = str; *p != '\0'; p++)

{

m[*p]++;

}

map::iterator iter = m.begin();

for(; iter != m.end(); iter++)

{

if((*iter).second == 1)

{

return (*iter).first;

}

}

return 0;

}

2.3.5 输出字符串中每个字符出现的次数

详见count_string.c

void printCountString(char *str)

{

int count[256] = {0};

for(char *p = str; *p; p++)

{

count[*p]++;

}

for(int i = 0; i < 256; i++)

{

if(count[i] > 0)

{

printf("The count of %c is: %d\n", i, count[i]);

}

}

}

2.3.6 输出字符串所有字符的组合

该题目未完成

有一个集合A = {a,b,c,d,e,f,g},要求从这个集合中仍取n个元素(n>=1 n<=7);

打印所有可能出现的组合!

n=1 a, b, c, d, e, f, g

n=2 ab, bc, ac, ae, fg,......

n=3 abc,abd,abe,........

2.3.7 实现C++ string类

代码如下,详见string/mystring.cpp。

#include

#include

using namespace std;

class String

{

friend ostream& operator<

//+运算符重载,必须声明为友元,因为二元运算符成员函数重载只能有一个参数或没有参数,而这里有两个参数

friend String operator+(const String& s1, const String& s2);

public:

String(const char *str = NULL);// 普通构造函数

String(const String& other);// 拷贝构造函数

~String();// 析构函数

String& operator=(const String& other);// 赋值运算符重载

String& operator+=(const String& other);// +=运算符重载

private:

char *m_data;// 用于保存字符串

};

String::String(const char *str)

{

if(str == NULL)

{

m_data = new char[1];

m_data[0] = '\0';

}

else

{

m_data = new char(strlen(str) + 1);

strcpy(m_data, str);

}

}

String::String(const String &other)

{

m_data = new char[strlen(other.m_data) + 1];

strcpy(m_data, other.m_data);

}

String::~String()

{

delete [] m_data;

}

String& String::operator=(const String& other)

{

if(this == &other)

return *this;

delete [] m_data;

m_data = new char[strlen(other.m_data) + 1];

strcpy(m_data, other.m_data);

return *this;

}

String& String::operator+=(const String& other)

{

int len = strlen(m_data) + strlen(other.m_data) + 1;

char* tmp = new char[len];

memset(tmp, 0, len);

strcpy(tmp, m_data);

strcat(tmp, other.m_data);

delete [] m_data;

m_data = new char(len);

strcpy(m_data, tmp);

delete [] tmp;

return *this;

}

String operator+(const String& s1, const String& s2)

{

String tmp(s1);

tmp += s2;

return tmp;

}

ostream& operator<

{

os << str.m_data;

return os;

}

int main()

{

String a("abcdefg");

cout << a << endl;

String b(a);

cout << b <

String c = a;

cout << c << endl;

a += c;

cout << a << endl;

String d = a + b;

cout << d << endl;

return 0;

}

2.4 排序算法

排序算法

最好时间复杂度

最坏时间复杂度

平均时间复杂度

空间复杂度

稳定性

备注

选择排序

O($$n^2$$)

O($$n^2$$)

O($$n^2$$)

O(1)

不稳定

n小时较好

插入排序

O(n)

O($$n^2$$)

O($$n^2$$)

O(1)

稳定

大部分有序时较好

冒泡排序

O(n)

O($$n^2$$)

O($$n^2$$)

O(1)

稳定

n小时较好

归并排序

O($$nlog_2n$$)

O($$nlog_2n$$)

O($$nlog_2n$$)

O(n)

稳定

n大时较好

快速排序

O($$nlog_2n$$)

O($$n^2$$)

O($$nlog_2n$$)

O($$log_2n$$)

不稳定

n大时较好

希尔排序

O(n)

O($$n^s$$) 1

O($$nlog_2n$$)

O(1)

不稳定

s是所选分组

堆排序

O($$nlog_2n$$)

O($$nlog_2n$$)

O($$nlog_2n$$)

O(1)

不稳定

n大时较好

2.4.1 选择排序

第一轮排序将最小的数放在a[0],第二轮排序将剩余数中最小的数放在a[1]...

以数组{38, 65, 97, 76, 13, 27, 49}为例:

第一轮排序:13 [65 97 76 38 27 49]

第二轮排序:13 27 [97 76 38 65 49]

第三轮排序:13 27 38 [76 97 65 49]

第四轮排序:13 27 38 49 [97 65 76]

第五轮排序:13 27 38 49 65 [97 76]

第六轮排序:13 27 38 49 65 76 [97]

代码如下,详见sort/select_sort.c。

void selectSort(int *a, int len)

{

int i = 0;

int j = 0;

int min = 0; //每一轮排序最小数的下标

int tmp = 0;

for(i = 0; i < len - 1; i++)

{

min = i;

for(j = i + 1; j < len; j++)

{

if(a[min] > a[j])

{

min = j;

}

}

if(min != i)

{

tmp = a[min];

a[min] = a[i];

a[i] = tmp;

}

}

}

2.4.2 插入排序

第一步取第一个数放在首位,第二步取第二个数和第一个数比较,小则插入第一个数前面,此时第一个数和第二个数变成了一个有序序列,第三步取第三个数插入有序序列的合适位置,并组成一个新的有序序列,以此类推。

以数组{38, 65, 97, 76, 13, 27, 49}为例:

第一趟排序:[38 65] 97 76 13 27 49

第二趟排序:[38 65 97] 76 13 27 49

第三趟排序:[38 65 76 97] 13 27 49

第四趟排序:[13 38 65 76 97] 27 49

第五趟排序:[13 27 38 65 76 97] 49

第六趟排序:[13 27 38 49 65 76 97]

代码如下,详见sort/insert_sort.c。

void insertSort(int *a, int len)

{

int i = 0;

int j = 0;

int tmp = 0;

for(i = 1; i < len; i++)

{

tmp = a[i];

for(j = i - 1; j >= 0; j--)

{

if(tmp < a[j])

{

a[j+1] = a[j];

}

else

{

break;

}

}

a[j+1] = tmp;

}

}

2.4.3 冒泡排序

第一趟排序将最大的数放在最后的位置,第二趟排序将第二大的数放在倒数第二的位置,以此类推...

以数组{65, 38, 97, 76, 13, 27, 49}为例:

第一趟排序:38 65 76 13 27 49 [97]

第二趟排序:38 65 13 27 49 [76 97]

第三趟排序:38 13 27 49 [65 76 97]

第四趟排序:13 27 38 [49 65 76 97]

第五趟排序:13 27 [38 49 65 76 97]

第六趟排序:13 [27 38 49 65 76 97]

代码如下,详见sort/bubble_sort.c。

void bubbleSort(int *a, int len)

{

int i = 0;

int j = 0;

int tmp = 0;

int flag = 0; //冒泡排序优化标志位

for(i = 0; i < len - 1; i++)

{

flag = 0;

for(j = 0; j < len - i - 1; j++)

{

if(a[j] > a[j + 1])

{

tmp = a[j];

a[j] = a[j + 1];

a[j + 1] = tmp;

flag = 1;

}

}

if(flag == 0)

break;

}

2.4.4 归并排序

第一步不停地划分子表(划分至每个子表两个元素),第二步不停地合并子表至完整地表。

以数组{49, 38, 65, 97, 76, 13, 27}为例:

第一次划归并后:[38 49] [65 97] [13 76] [27]

第二次划归并后:[38 49 65 97] [13 27 76]

第三次划归并后:[13 27 38 49 65 76 97]

代码如下,详见sort/merge_sort.c。

void merge(int *a, int begin1, int end1, int begin2, int end2, int *tmp)

{

int index = begin1;

int i = begin1, j = begin2;

//注意:当划分的区间足够小时,begin1==end1,begin2==end2

while (i <= end1&&j <= end2){

if (a[i]<=a[j])

tmp[index++] = a[i++];

else

tmp[index++] = a[j++];

}

//将左边元素填充到tmp中

while (i <= end1)

tmp[index++] = a[i++];

//将右边元素填充的tmp中

while (j <= end2)

tmp[index++] = a[j++];

//将tmp中的数据拷贝到原数组对应的序列区间

//注意:end2-begin1+1

memcpy(a + begin1, tmp + begin1, sizeof(int)*(end2 - begin1 + 1));

}

void mergeSort(int *a, int left, int right, int *tmp)

{

if (left >= right)

return;

//mid将数组二分

int mid = left + ((right - left) >> 1);

//左边归并排序,使得左子序列有序

mergeSort(a, left, mid, tmp);

//右边归并排序,使得右子序列有序

mergeSort(a, mid + 1, right, tmp);

//将两个有序子数组合并

merge(a, left, mid, mid + 1, right, tmp);

}

2.4.5 快速排序

选择数组第一个元素作为关键字,比它小的放它左边,比它大的放它右边,再对左右两边的元素进行同样的操作。

以数组{65, 38, 97, 76, 13, 27, 49}为例:

第一次交换后:[49 38 27 13] 65 [76 97]

第二次交换后:[13 38 27] 49 65 [76] 97

第三次交换后:13 [38 27] 49 65 76 97

第四次交换后:13 [27] 38 49 65 76 97

代码如下,详见sort/quick_sort.c。

void swap(int *a, int *b)

{

*a = *a + *b;

*b = *a - *b;

*a = *a - *b;

}

void quickSort(int *a, int low, int high)

{

int i = low;

int j = high;

int key = a[low];

if(low >= high)

return;

while(low < high)

{

while(low < high && key <= a[high])

{

high--;

}

if(key > a[high])

{

swap(&a[low], &a[high]);

low++;

}

while(low < high && key >= a[low])

{

low++;

}

if(key < a[low])

{

swap(&a[low], &a[high]);

high--;

}

}

quickSort(a, i, low - 1);

quickSort(a, low + 1, j);

}

2.4.6 希尔排序

希尔排序是建立在插入排序的基础上的,如果数组有10个元素,先分成10/2=5个子序列,步进为5,那么1和6,2和7,3和8,4和9, 5和10各自成对,对这5个子序列进行插入排序;排完之后,再分成5/2=2个子序列,步进为2,并分别进行插入排序;最后分成2/2=1个子序列,步进为1,进行一次插入排序即可。

以数组{65, 38, 97, 76, 13, 27, 49, 18}为例:

步进8/2=4,第一次插入排序后:13 27 49 18 65 38 97 76

步进4/2=2,第二次插入排序后:13 18 49 27 65 38 97 76

步进2/2=1,第三次插入排序后:13 18 27 38 49 65 76 97

代码如下,详见sort/shell_sort.c。

void shellSort(int *a, int len)

{

int i = 0;

int j = 0;

int step = len/2;

int tmp = 0;

while(step >= 1)

{

//插入排序

for(i = step; i < len; i++)

{

tmp = a[i]; //设置哨兵

for(j = i - step; j >= 0; j -= step)

{

if(tmp < a[j])

{

a[j+step] = a[j];

}

else

{

break;

}

}

a[j+step] = tmp;

}

step /= 2; //调整步进

}

}

2.4.7 堆排序

堆排序也称为二叉树排序,需要用到完全二叉树(数组实现)的知识。将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将其与末尾元素进行交换,此时末尾就为最大值;然后将剩余n-1个元素重新构造成一个大顶堆,这样会得到n个元素的次小值;如此反复执行,便能得到一个有序序列了。

每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;而每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆

此种排序较难理解,请看图解二叉树。

代码如下,详见sort/heap_sort.c。

void swap(int *a, int *b)

{

*a = *a + *b;

*b = *a - *b;

*a = *a - *b;

}

//调整大顶堆

void adjustMaxHeap(int *a, int pos, int len)

{

int child = 0;

int tmp = 0;

for(tmp = a[pos]; 2 * pos + 1 <= len; pos = child)

{

child = 2 * pos + 1; //从pos结点的左孩子开始,即 pos * 2 + 1

if(child < len && a[child] < a[child + 1])

child++; //如果左孩子小于右孩子,child指向右孩子

if(a[child] > tmp)

a[pos] = a[child]; //如果子结点大于父结点,那么子结点赋值给父结点

else

break;

}

a[pos] = tmp; //将tmp放到最终的位置

}

//大顶堆排序

void maxHeapSort(int *a, int len)

{

int i = 0;

//1. 构建大顶堆

for(i = len/2 - 1; i >= 0; i--)

{

adjustMaxHeap(a, i, len - 1); //从第一个非叶子结点从下至上,从右至左调整

}

//2. 交换堆顶和末尾元素,并调整大顶堆

for(i = len - 1; i > 0; i--)

{

swap(&a[0], &a[i]); //交换堆顶和末尾元素

adjustMaxHeap(a, 0, i - 1); //调整大顶堆

}

}

2.5 其它类型编程题

2.5.1 如何判断机器是大端还是小端

大端:高字节存在低地址,低字节存在高地址;

小端:高字节存在高地址,低字节存在低地址。

代码如下,详见other/big_little_endian.c。

#include

union un

{

char ch;

int data;

};

int main()

{

union un u;

u.data = 0x12345678;

if(u.ch == 0x12)

{

printf("Big Endian!\n");

}

else if(u.ch == 0x78)

{

printf("Little Endian!\n");

}

else

{

printf("Unknown!\n");

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值