一、数组
1、二分法查找(704题)
注意“合法区间”是“左闭右闭”还是“左闭右开”!!!
'''左闭右闭'''
int BinarySearch(vector<int> nums, int target){
int left = 0, right = nums.size() - 1; // 1、right取值为闭区间
int mid = (left + right) / 2;
while(left <= right){ // 2、比较符号为<=
if(nums[mid] <= target){
left = mid + 1;
mid = (left + right) / 2;
}
else if(nums[mid] > target){
right = mid - 1; // 3、同样right取值为闭区间
mid = (left + right) / 2;
}
else return mid;
}
return -1;
}
'''左闭右开'''
int BinarySearch(vector<int> nums, int target){
int left = 0, right = nums.size(); // 1、right取值为开区间
int mid = (left + right) / 2;
while(left < right){ // 2、比较符号为<
if(nums[mid] <= target){
left = mid + 1;
mid = (left + right) / 2;
}
else if(nums[mid] > target){
right = mid; // 3、同样right取值为开区间
mid = (left + right) / 2;
}
else return mid;
}
return -1;
}
时间复杂度:O(logn)
空间复杂度:O(1)
2、排序算法
#include<iostream>
#include<vector>
using namespace std;
void swap(int &a, int &b){
int c = a;
a = b;
b = c;
}
// 冒泡排序
void BubbleSort(vector<int> &nums){
int len = nums.size();
for(int i = 0; i < len - 1; i++){
for(int j = 0; j < len - i - 1; j++){
if(nums[j] > nums[j + 1]){
swap(nums[j], nums[j + 1]);
}
}
}
}
// 选择排序
void SelectionSort(vector<int> &nums){
int len = nums.size();
for(int i = 0; i < len - 1; i++){
int minIndex = i;
for(int j = i; j < len - 1; j++){
if(nums[j + 1] < nums[minIndex]){
minIndex = j + 1;
}
}
if(i != minIndex) swap(nums[i], nums[minIndex]);
}
}
// 插入排序
void InsertionSort(vector<int> &nums){
int len = nums.size();
for(int i = 0; i < len; i++){
int pre = i - 1;
int current = nums[i];
while(pre >= 0 && current < nums[pre]){
nums[pre + 1] = nums[pre];
pre--;
}
nums[pre + 1] = current;
}
}
int main(){
int a[] = {8,6,5,3,2,8,4,6,3,1,5,2,3,5,1,2};
vector<int> v;
for(int i = 0; i < sizeof(a) / sizeof(int); i++){
v.push_back(a[i]);
}
// BubbleSort(v);
// SelectionSort(v);
InsertionSort(v);
for(vector<int>::iterator it = v.begin(); it < v.end(); it++){
cout << *it << " ";
}
return 0;
}
3、移除元素(27题)
方法一:相向指针法
考虑到pop_back()方法的作用是将vector容器中末尾的元素删去,所以想到同时从数组两边进行判定并删除元素。
//方法一
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int len = nums.size();
if(len == 0) return 0;
int j = len - 1; //尾指针
for(int i = 0; i <= j; i++){
if(nums[i] == val){ //当头指针遍历至val值时,需要现将尾部所有的val值先删去
while(nums[j] == val && j != i){
nums.pop_back();
j--;
}
if(nums[j] != val){
//此时尾指针指向的元素必定不为val值,先进行交换把头指针指向的val值换到末尾
int tmp = nums[j];
nums[j] = nums[i];
nums[i] = tmp;
nums.pop_back(); //再把末尾的val值删掉
j--;
}
else{
//由于前面的while循环跳出条件有可能是i与j相等,
//此时数组中还存在一个头尾指针共同指向的val值,故将其删去
nums.pop_back();
j--;
}
}
}
return nums.size();
}
};
这种方法改变了原数组中元素的相对位置。
时间复杂度:O(n)
空间复杂度:O(1)
方法二:快慢指针法
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组。
- 慢指针:当快指针指向非目标元素时慢指针才会移动。
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0, fast;
for(fast = 0; fast < nums.size(); fast++){
if(nums[fast] != val){
nums[slow++] = nums[fast];
}
}
return slow;
}
};
这种方法没有改变原数组中元素的相对位置。
时间复杂度:O(n)
空间复杂度:O(1)
二、链表
1、移除链表元素(203题)
方法一:原链表法(需要特别考虑头节点的处理)
/**
* 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:
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr) return nullptr;
while(head != nullptr && head->val == val){
ListNode* tmp = head; //删除元素空间的写法
head = head->next;
delete tmp;
}
ListNode* cur = head;
while(cur != nullptr && cur->next != nullptr){
if(cur->next->val == val){
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else cur = cur->next;
}
return head;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
方法二:虚拟头节点法(在原链表前加一个虚拟头节点,处理上一视同仁,推荐)
/**
* 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:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy = new ListNode(0, head); //构建一个指向原链表的虚拟头节点
ListNode* cur = dummy;
while(cur != NULL && cur->next != NULL){
if(cur->next->val == val){
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else cur = cur->next;
}
return dummy->next; // 注意返回虚拟头节点的下一个节点,才是真正的原链表头节点
}
};
时间复杂度:O(n)
空间复杂度:O(1)
2、设计链表(707题)
class MyLinkedList {
public:
struct LinkNode {
int val;
LinkNode* next;
LinkNode(int val): val(val), next(nullptr) {}
};
MyLinkedList() {
dummyHead = new LinkNode(0);
len = 0;
}
int get(int index) {
if(index < 0 || index > len - 1) return -1;
LinkNode* cur = dummyHead->next;
while(index--) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkNode* first = new LinkNode(val);
first->next = dummyHead->next;
dummyHead->next = first;
len++;
}
void addAtTail(int val) {
LinkNode* end = new LinkNode(val);
LinkNode* cur = dummyHead;
int count = len;
while(count--) {
cur = cur->next;
}
cur->next = end;
len++;
}
void addAtIndex(int index, int val) {
if(index > len || len < 0) return;
LinkNode* newNode = new LinkNode(val);
LinkNode* cur = dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
len++;
}
void deleteAtIndex(int index) {
if(index > len - 1 || index < 0) return;
LinkNode* cur = dummyHead;
while(index--) {
cur = cur->next;
}
LinkNode* tmp = cur->next;
cur->next = tmp->next;
delete tmp;
len--;
}
private:
LinkNode* dummyHead;
int len;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
时间复杂度: 涉及 index 的相关操作为 O(index), 其余为 O(1)
空间复杂度: O(n)