力扣 回文链表-234
回文链表-234
const int N = 1e5+5;
int a[N];//定义一个整形的全局数组作为辅助数组存储链表反转前的值
class Solution {
/*本题的解题思路是先将链表中每个值存储到辅助数组a中,然后反转链表,
最后,反转后链表的值和没反转之前的值(存在辅助数组中的值)一一作比较,
如果每个值都相同则为回文链表返回true,否则返回false*/
public:
int i = 0;//i作为循环中数组的下标
bool isPalindrome(ListNode* head) {
//先将头节点head的位置存储起来,因为循环存储链表反转前的值时,head的位置会发生变化,就无法进行后续反转链表操作了
ListNode* originalHead = head;
//将链表中的每个节点的值存储到数组a中
while(head!=NULL)
{
a[i] = head->val;
head = head->next;
++i;
}
//以下是反转链表操作
ListNode* cur = NULL;//定义一个ListNode类型的指针指向头节点的左一个位置,作为反转后链表的结尾
ListNode* pre = originalHead;//定义一个ListNode类型的指针指向头节点位置
//while循环当pre指向最后一个节点的右一个节点(NULL)时,全部反转完成,cur正好指向最后一个节点作为反转后的头节点
while(pre!=NULL)
{
//定义一个ListNode类型的指针t用来存储指针pre指向的下一个位置以便在修改当前节点的指针后能够继续遍历链表
ListNode* t = pre->next;
//进行局部反转,将pre的下一个节点改为指向上一个节点(cur)
pre->next = cur;
//局部反转过后进行右移,下一次循环之后继续pre,cur两个指针进行局部反转
cur = pre;
//局部反转后pre也进行右移,始终保持cur在左pre在右
pre = t;
//注意:如果不将pre的下一个节点存储在t中,那么在经过pre->next = cur后就无法再访问到原来的下一个节点了。
}
/*最后反转前的链表每个节点的值(存放在数组a中)与反转后链表每个节点的值作比较,
只要遇到不同的就直接返回false,如果一直到循环结束都没遇到不同的值,则返回true*/
for(int j = 0;j < i;j++)
{
if(cur->val!=a[j])return false;
cur = cur->next;
}
return true;
}
};
每日问题
C C++ 中有哪些类型转换方式? 分别有什么区别?
1.隐式类型转换(自动类型转换)
定义:这是编译器自动进行的类型转换,不需要程序员显示地操作。它主要遵循一定的规则,在不丢失数据精度(或者在合理范围内丢失精度)的情况下,将一种数据类型转换为另一种数据类型。
转换规则示例:
算数运算中的转换:在进行算数运算时,如果操作数类型不同,编译器会自动进行转换。例如,在int + double的运算中,int类型的值会自动转换为double类型,然后进行加法运算。像3+3.5,编译器会将3转换为double类型的3.0,再和3.5相加,结果为6.5.
赋值运算中的转换:当把一个值赋给一个变量时,如果值的类型和变量类型不一致,也可能发生隐式转换。例如,将一个int值赋给一个double变量,int值会自动转换为double类型。如double d = 5;,这里5会自动转换为5.0后赋给d。但是,将一个较大范围类型的值赋给一个较小范围类型的变量时,可能会丢失数据精度,如int i = 3.9;,此时3.9会被截断为3赋给i。
特点:
便利性:隐式转换使得代码编写更加方便,程序员不需要手动处理很多常见的类型转换情况,编译器会自动按照规则进行处理。
潜在风险:可能会导致数据精度丢失或不符合预期的结果。特别是在涉及不同的混合运算和赋值时,需要注意数据类型的范围和精度变化。
2.显示类型转换(强制类型转换)
定义:这是程序员通过特定的语法手动进行的类型转换,用于再需要明确改变数据类型的情况下使用。
C语言中的强制类型转换方式:
基本语法:使用(目标类型)表达式的形式,例如,(int)3.14会将3.14强制转换为int类型,结果为3。
指针类型转换示例:在C语言中,还可以进行指针类型的强制转换。例如,假设有一个void*类型的指针void* p;,如果想将它转换为int*类型,可以使用(int*)p。但这种转换需要谨慎,因为如果转换不当可能会导致程序出错,如访问非法内存等情况。
这是最通用的类型转换方式,适用于所有基本数据类型和指针类型。但它也是最不安全的,因为它会进行尽可能宽松的转换,不考虑类型之间的兼容性和潜在的数据丢失。
C++语言中的强制类型转换方式(更安全和明确的方式):
1.静态类型转换(Static Case):
语法:static_cast(expression)
静态类型转换用于在具有明确、安全的转换路径的类型之间进行转换。它同通常用于基本数据类型之间的转化、相关类之间的转换(如基类与派生类之间的转换,前提是转换时安全的),以及void* 与其他指针类型之间的转换。
double a = 100.5;
int b = static_cast<int>(a);//将double转换为int,可能丢失小数部分
2.动态类型转换(Dynamic Cast)
语法:dynamic_cast(expression)
动态类型转换用于在运行时检查类对象的实际类型,并安全地向上或向下的类层次结构转换。它主要用于多态类型(即包含虚函数的类)。如果转换失败,dynamic_cast会返回nullptr(对于指针)或抛出std::bad_cast异常(对于引用)。
class Base{ virtual void func(){}};
class Derived : public Base{};
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);//将Base*转换为Derived*
3.常量类型转换(Const Cast)
语法:const_cast(expression)
常量类型转换用于去除或添加常量性(const 或 volatile)。它主要用于将 const 指针转换为非 const 指针(或反之),以便能够修改原本不可修改的数据。
const int* constPtr = &someValue;
int* nonConstPtr = const_cast<int*>(constPtr);//去除 const 属性
4.重解释类型转换(Reinterpret Cast)
语法:reinterpret_cast(expression)
重解释类型转换用于进行最低级别的转换,它基本上只是重新解释位模式。这种转换通常是不安全的,因为它几乎不进行任何检查,只是简单地告诉编译器将一种类型视为另一种类型。它主要用于底层编程(如与硬件接口)和与C代码的互操作。
最低级别的转换:
reinterpret_cast提供了C++中最低级别的类型转换能力。它几乎不进行任何类型检查,只是简单地告诉编译器:“我知道我在做什么,请把这个指针或引用当作另一种类型来处理。”
重新解释位模式:
当使用reinterpret_cast进行转换时,原始数据的位模式保持不变。这意味着,如果原始数据是以一种类型存储的,而转换后的类型以不同的方式解释这些位,那么得到的结果可能是完全不可预测的。
位模式:位模式,又称位流或位序列,是计算机内部数据的一种表示方式。它由一系列的二进制位(bit)组成,每个位只能是0或1。这些二进制位按照特定的顺序排列,形成了可以表示各种数据类型的位模式。
位模式数据表示:不同的位模式可以表示不同的数据类型,如整数、实数、字符等。通过特定的编码方式,位模式还可以表示更复杂的数据结构,如结构体、联合体等。
位模式应用:数据类型转换:在编程中,有时需要将一种数据类型的变量转换为另一种数据类型。这时,可以通过重新解释位模式的方式来实现类型转换。但需要注意的是,这种转换可能会导致数据丢失或精度下降,因此在使用时需要谨慎。
int a = 100;
void* ptr = &a;
int* intPtr = reinterpret_cast<int*>(ptr);//将 void* 转换为 int*
区别总结:
C风格的强制类型转换:通用但不安全,会进行尽可能宽松的转换。
静态类型转换:在编译时进行,用于明确且安全的类型转换。
动态类型转换:在运行时进行,用于多态类型的向上或向下转换,提供安全类型检查。
常量类型转换:用于添加或去除常量性(const 或 volatile)。
重解释类型转换:最低级别的转换,只是重新解释位模式,通常不安全。
选择哪种类型转换方式取决于具体的场景和安全性要求。在可能的情况下,应优先使用更安全的类型转换方式(如 static_cast 和 dynamic_cast),并尽量避免使用 C 风格的强制类型转换和 reinterpret_cast。
类型转换可能会带来哪些问题?
一、数据丢失
原因:一种数据类型的范围可能比另一种数据类型小。在将一个范围较大的数据类型转换为一个范围较小的数据类型时,超出目标类型表示范围的数据将被截断或丢弃。
示例:将一个浮点数转换为整数时,小数部分将被截断。将一个长字符串转换为一个短的VARCHAR类型时,超出长度的部分将被丢弃。
二、精度问题
原因:在进行数值类型的转换时,特别是从高精度类型向低精度类型转换时,精度可能会受到影响。
示例:将一个DECIMAL类型转换为FLOAT类型时,由于浮点数的二进制表示法无法准确地表示某些十进制小数,可能会导致精度丢失。
三、内存溢出
原因:在进行强制类型转换时,如果目标类型的内存表示比源类型大,而分配的内存不足以容纳转换后的数据,可能会导致内存溢出。
示例:在某些情况下,将一个较小的数据类型转换为较大的数据类型时,如果内存管理不当,可能会引发内存溢出错误。
四、运行时错误
原因:强制类型转换可能会引发运行时错误,特别是在类型之间存在不兼容的情况下。
示例:试图将一个类型转换为与其不兼容的类型时,将会引发异常或错误,并导致程序中断。例如,将一个字符串强制转换为整数时,如果字符串中包含非数字字符,转换将失败并抛出异常。
五、逻辑错误
原因:在进行类型转换时,如果开发者没有正确理解数据类型之间的关系或转换规则,可能会导致逻辑错误。
示例:错误地假设类型转换后的数据具有与原始数据相同的含义或行为,从而在程序中引入逻辑错误。
六、代码可读性降低
原因:过多地使用强制类型转换可能会降低代码的可读性,使代码变得难以理解和维护。
示例:当代码中存在大量的强制类型转换时,读者可能难以理解其含义和目的,从而增加程序的调试难度和维护成本。
七、安全性问题
SQL注入:在数据库操作中,从用户输入或其他不可信的来源获取数据时,不安全的类型转换可能导致SQL注入攻击。
代码注入:在动态生成代码时,不安全的类型转换可能导致代码注入攻击。