重排链表-143
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
//利用快慢指针找到链表的中点
//慢指针走一步,快指针走两步,快指针走到头循环结束后,慢指针的位置就是链表的中点
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
//反转链表
ListNode* cur = NULL;
ListNode* pre = slow->next;
slow->next = NULL;//反转链表之前要切断前半部分
while (pre != NULL){
ListNode* t = pre->next;
pre->next = cur;
cur = pre;
pre = t;
}
//合并链表,将后半部分反转之后的链表中的每个节点插入到前半部分链表每个节点的中间位置
ListNode* h = head;
ListNode* c = cur;
while (c != NULL)
{
/*将前半链表和后半部分链表的当前节点的下一个节点保存下来,
以便后续的连接操作,因为后面会改变指向,会找不到下一个节点
*/
ListNode* temph = h->next;
ListNode* tempc = c->next;
/*
将前半部分链表的h节点的下一个节点指向c,将后半部分链表的c节点的下一个节点指向temph,
就是前半部分链表的h节点的下一个节点(没指向c节点之前的下一个节点)
*/
h->next = c;
c -> next = temph;
/*
改变指向过后,更新h和c为原来节点(前半部分链表和后半部分链表都没改变指向之前)的下一个节点
*/
h = temph;
c = tempc;
//继续进行循环拼接,直到c为NULL结束循环,则拼接完成
}
}
};
每日问题
什么是多态性?C++中如何实现多态?多态性的好处是什么?
什么是多态性?
多态性(Polymorphism) 是面向对象编程中的一个重要概念,指的是同一个操作或方法可以作用于不同类型的对象,并表现出不同的行为。它使得一个接口可以根据对象的实际类型进行不同的操作。
多态的两种类型:
1.编译时多态(静态多态,早绑定):
在编译时决定调用哪个函数。
主要通过函数重载(Function Overloading)和运算符重载(Operator Overloading)实现。
2.运行时多态(动态多态,晚绑定):
在运行时根据对象的实际类型来决定调用哪个函数。
主要通过虚函数(Virtual Functions)和继承关系来实现。
C++中如何实现多态?
1. 编译时多态(静态多态)
函数重载(Function Overloading):允许多个同名函数根据参数个数或类型不同而有不同的实现。
cpp
#include <iostream>
using namespace std;
class Printer {
public:
void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double d) {
cout << "Printing double: " << d << endl;
}
};
int main() {
Printer p;
p.print(10); // 调用 print(int)
p.print(3.14); // 调用 print(double)
return 0;
}
运算符重载(Operator Overloading):允许自定义运算符的行为。
cpp
#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
Complex(int r, int i) : real(r), imag(i) {}
// 重载加法运算符
Complex operator + (const Complex &other) {
return Complex(real + other.real, imag + other.imag);
}
void display() {
cout << real << " + " << imag << "i" << endl;
}
};
int main() {
Complex c1(2, 3), c2(4, 5);
Complex c3 = c1 + c2; // 调用重载的 + 运算符
c3.display(); // 输出: 6 + 8i
return 0;
}
2. 运行时多态(动态多态)
虚函数(Virtual Function) 和 继承: C++ 的多态性主要通过基类指针或引用指向派生类对象并通过 虚函数 实现。虚函数允许子类重写父类的函数,并在运行时根据对象的实际类型调用相应的函数。
cpp
#include <iostream>
using namespace std;
// 基类
class Animal {
public:
virtual void sound() { // 虚函数
cout << "Animal makes a sound" << endl;
}
};
// 派生类
class Dog : public Animal {
public:
void sound() override { // 重写虚函数
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void sound() override { // 重写虚函数
cout << "Cat meows" << endl;
}
};
int main() {
Animal* animal1 = new Dog(); // 基类指针指向派生类对象
Animal* animal2 = new Cat(); // 基类指针指向另一个派生类对象
animal1->sound(); // 调用 Dog 类的 sound() 函数
animal2->sound(); // 调用 Cat 类的 sound() 函数
delete animal1;
delete animal2;
return 0;
}
输出:
cpp
Dog barks
Cat meows
在上面的例子中:
sound() 是一个虚函数。
通过基类 Animal 的指针 animal1 和 animal2 来调用派生类 Dog 和 Cat 中的 sound() 方法。
由于 sound() 是虚函数,程序会在运行时动态绑定正确的函数调用(即调用 Dog 或 Cat 类中的重写版本),而不是基类的 sound()。
如何实现运行时多态:
1.虚函数:
在基类中声明函数为 virtual,表示该函数希望被子类重写。
2.重写:
子类通过 override 关键字或仅通过函数签名重写父类的虚函数。
3.多态:
使用基类指针或引用调用虚函数,实际调用的是派生类中的实现。
多态性的好处:
1.代码的扩展性:
多态使得程序在增加新功能时不需要修改现有代码。你可以新增子类,而不需要修改调用基类的代码。
2.提高代码的灵活性和可维护性:
通过多态,程序可以更加灵活地根据对象的实际类型执行不同的操作,减少了条件语句或类型检查。
3.实现接口和抽象:
多态允许定义统一的接口或抽象类,而具体的行为则由不同的子类实现。这可以使得代码在使用时更加一致且易于理解。
4.降低耦合度:
多态性使得不同模块之间的耦合度降低,因为你可以通过基类接口与派生类进行交互,而不需要关心派生类的具体实现。
多态性的注意事项:
1.虚函数的使用:
在基类中定义的虚函数通常需要在派生类中被重写,否则可能会调用基类的实现,导致不符合预期的行为。
2.析构函数:
如果类有虚函数,通常也需要将析构函数定义为虚函数,以确保在删除通过基类指针指向派生类对象时能够正确调用派生类的析构函数,从而避免资源泄漏。
3.性能开销:
使用虚函数可能带来一些性能开销,因为每次调用虚函数时都需要通过虚表(VTable)进行动态查找。
总结:
1.多态性 是面向对象编程的重要特性,允许通过统一的接口操作不同类型的对象,且表现出不同的行为。
2.在 C++ 中,编译时多态通过函数重载和运算符重载实现,运行时多态通过虚函数和继承实现。
3.多态性增强了代码的灵活性、扩展性、可维护性,减少了耦合度。