Data structure (C + +)_ Tsinghua (Deng Junhui) ~ ~ very detailed~

Posted by canobi on Wed, 26 Jan 2022 16:36:07 +0100

Data structure (I) school online link.
Under data structure (school online).
Station B link.

Method steps:
1. Watch the video of station B, type the code and take notes. Online segmentation according to school
2. Compare whether the online video of the school is missing.
3. Do school online exercises and supplement notes.

Chapter - Introduction

calculation
Object: rules and skills
Objective: high efficiency and low consumption
Computers: tools and means
Calculation is the goal

Rope computer and its algorithm

  • Input: any given line l and its upper point A
  • Output: A vertical line passing through l made by A

Algorithm (ancient Egypt):
1. Take 12 equal length ropes and connect them into a ring
2. From point A, straighten 4 sections of rope along l and fix it to point B
3. Find the end point C of the third rope in the other direction
4. Move point C and straighten the remaining 3 + 5 ropes.

  • By Pythagorean theorem: the length ratio of three sides is 3:4:5, which must form a right triangle
  • Fix point B first (the straight line is known and easy to determine). Then there is point C (note that one side has 3 segments and one side has 5 segments, which is fixed, so point C can also be determined)

Repeat the process mechanically

Ruler and gauge computer and its algorithm

  • Give any line segment AB on the plane (input) and divide it into three equal parts (output)

Algorithm:
1. A ray that does not coincide with AB is emitted from a ρ
2. In ρ Take AC '= C'd' = D 'B`
3. Connection B 'B
4. Make parallel lines of B through d 'and intersect AB at D
5. Make a parallel line of B 'B through C', and intersect AB at C

  • Similar triangle theorem

It is decomposed into several steps and executed mechanically.

P4
Calculation = information processing
With the help of a certain tool, in accordance with certain rules, in a clear and mechanical form.

Algorithm: a sequence of instructions designed to solve a specific problem under a specific computing model.
Input, output, correctness, certainty, feasibility, finiteness.

P5

  • Occasionally rise, overall decline
int halistone(int n) {// Calculate the length of the sequence Hailstone(n)
	int length = 1;
	while (n > 1) {
		(n % 2) ? n = 3 * n + 1 : n /= 2; length++;
	}
	return length;   
}
  • The length is not proportional to n
    Some n results are infinite. Whether it is an algorithm is controversial
  • Program ≠ algorithm
  • Dead loop or stack overflow

P6 good algorithm
Efficiency: as fast as possible; The storage space shall be as small as possible.

P7
Data Structure + Algorithm (DSA)
measure

To measure is to know.
If you can not measure it,
you can not improve it.
— Lord Kelvin

P8
Cost: time + storage space
scale
P9
P10
P11

Turing machine (TM)

  • Tape is evenly divided into cells in turn, each marked with a certain character, and the default is' # '
  • Head: always aim at a cell and can read and rewrite characters in it. After each beat, you can turn to the adjacent grid on the left or right.
  • State: TM (Turing machine) is always in one of the limited states. After each beat, it can turn to another state (according to the rules).
  • Transition Function: (q, c; d, L/R,p): if the current status is q and the current character is c, rewrite the current character to d; Turn to the adjacent grid on the left / right; Enter p status. Once it turns into a specific state 'h', it stops.

P12 ?

TM : Increase
Function: add one to binary non negative integer

P13 RAM(Random Access Machine)
1. There is no limit on the total number of registers.
2. Each basic operation requires only constant time

P14 RAM instance?




Conclusion:

  • The execution process can be recorded as a table
  • The number of rows in the table is the total number of basic instructions executed

P15

Mathematic is more in need of good notations than of new theorems.
----Alan Turing
Good sign > New Theory

A good book seeks no understanding
Every time you know something, you will gladly forget to eat
----Tao Yuanming

  • Potential to solve large-scale problems (long term)
  • Focus on key aspects (mainstream)

For scale n input
1. Basic operands to perform: T(n)
2. Number of storage units to be occupied: S(n) (usually not considered)

P16
enlarge
Upper bound:

Lower bound (the best case of the algorithm)

P17

1. O(1): without steering (loop, call, recursion, etc.), it must be executed in sequence

2. O(logn) is very effective, and the complexity is infinitely close to a constant
3,O(n^c)
4. Linear O(n)
5. T(n) = a^n, intolerable
P20

Example: 2-Subset

S contains n positive integers and the sum is 2m
Whether S has subset T, and its sum is m

1. Intuitive algorithm: enumerate each subset of S one by one and count the sum of elements in it.

  • Iteration 2^n round

2- Subset is NP-complete
As far as the current calculation model is concerned, there is no algorithm that can answer this question in polynomial time

P21


P22

He calculated just as men breathe,
as eagles sustain themselves in the air.
— Francois Arago

eagles: Eagle

Two main tasks of algorithm analysis: correctness (invariance) × Monotonicity) + complexity.
Branch turn
Iterative cycle
Call + recursion (self call)

Main methods of complexity analysis:
1. Iteration: summation of series

  • (1) Arithmetic series: of the same order as the square of the last term
  • (2) Power series: one order higher than the power
  • (3) Geometric series (a > 1): of the same order as the last term.
  • (4) Fractional series: O(1)
    2. Recursion: recursive tracking + recursive equation
    3. Guess + verify




P24 cycle
Rectangular area


Example: take non extreme elements

Question:
Given integer subset S, | s | = n > = 3
Find the element a ∈ S, a ≠ max(S) and a ≠ min(S)

Example: bubble sorting

Problem: given n integers, arrange them in (non descending) order.

method:
Scan exchange: compare each pair of adjacent elements in turn and exchange them if necessary

/* Bubble sorting */
void bubblesort(int A[], int n) {
	for (bool sorted = false; sorted = !sorted;n--)  /* Flipping and self subtraction*/
		for(int i=1; i< n; i++)
			if (A[i - 1] > A[i]){
				swap(A[i - 1], A[i]);     
				sorted = false;    // Clear global order flag
			}
}
  • 1. Invariance: after K rounds of scanning and exchange, the largest k elements must be in place
  • 2. Monotonicity: after k rounds of scanning and exchange, the scale of the problem is reduced to n-1
  • 3. Correctness: after up to n scans, the algorithm must terminate and can give the correct solution

P27 back - of - the - envelope calculation

P29 iteration and recursion

Iteration is artificial, and recursion is the magic power.
To iterate is human, to recurse, divine.

Iteration is sometimes more efficient in practical applications

Governing the masses is like governing the few, so is the score.
The control of a large force is the same principle as the control of a few men:
it is merely a question of dividing up their numbers.

Divide and rule: divide a large scale into sub blocks

/* Array summation: iterations*/
/* Problem: calculate the sum of any n integers */
int SumI(int A[], int n) {
	int sum = 0;   // O(1) / / accumulator
	for (int i = 0; i < n; i++)    //O(n)
		sum += A[i];     // O(1)
	return sum;        // O(1)
}
/*  T(n) = O(n)
* S(n) = O(2)
*/
  • Gradually erode and continuously reduce the effective scale of the problem
    P30
    Reduce - and - conquer
  • Two sub problems: one is ordinary, the other is scale reduction.

Ordinary problem: it refers to a problem that can give results directly without complex operation.

P31

/* Array summation: linear recursion*/
sum(int A[], int n) {
	return (n < 1) ? 0 : sum(A, n - 1) + A[n - 1];
}
/*T(n) = O(1) * (n+1)  = O(n) */

Recursive trace analysis

  • Check each recursive instance and accumulate the required time (the calling statement itself is included in the corresponding sub instance), and the sum is the algorithm execution time.

Recursive tracing: applies only to concise recursive patterns
Recursive equation: more suitable for complex recursive mode

P32

  • The last item is T(0)-0

Example: array inversion

Give array A[0, n] and reverse it
Unified interface: void reverse(int *A, int lo, int hi);

/* Array inversion */

/* 1,Recursive version */
if (lo < hi)  // The parity of the problem scale remains unchanged, and two recursive bases are required
{
	swap(A[lo], A[hi]);   reverse(A, lo + 1, hi - 1);
}
else  return;

/* Iterative primitive method */
next:
if (lo < hi) {
	swap(A[lo], A[hi]); lo++; hi--;  goto next;
}

/* 3, Iterative compact */
while (lo < hi)  swap(A[lo++], A[hi--]);

P34 divide and rule

Divide - and - conquer
In order to solve a large-scale problem, it can be divided into several subproblems with roughly the same scale. The subproblems are obtained respectively, and the solution of the original problem is obtained from the solution of the subproblem.

/* Array summation: bisection recursion*/
sum(int A[]. int lo, int hi) {//Interval range A[lo, hi]
	if (lo == hi)    return A[lo];
	int mi = (lo + hi) >> 1;
	return sum(A, lo, mi) + sum(A, mi + 1, hi);
}// The entry form is sum(A, 0, n-1)




P36

Example: the two largest numbers

Find the two largest integers A[x1] and A[x2] from the array interval A[lo, hi], and A[x1] ≥ A[x2]

/* top 2 element in array*/
void max2(int A[], int lo, int hi, int& x1, int& x2) {
	for (x1 = lo, int i = lo + 1; i < hi; i++)
		if (A[x1] < A[i])   x1 = i;
	for (x2 = lo, int i = lo + 1; i < x1; i++)  /* Scan A[lo, x1]*/
		if (A[x2] < A[i])  x2 = i;
	for (int i = x1 + 1, i < hi; i++)   /* Scan A[x1, hi]*/
		if (A[x2] < A[i]) x2 = i;
}
/* T(n) = O(2n-3)*/

/* Improvement: maintain two pointers */
void max2(int A[], int lo, int hi, int& x1, int& x2) {
	if (A[x1 = lo] < A[x2 = lo + 1])   swap(x1, x2);
	for (int i = lo + 2; i < hi; i++)
		if (A[x2] < A[i])   // Compare with x2 first
			if (A[x1] < A[x2 = i])
				swap(x1, x2);
}
/* Best case: 1 + (n-2) *1 = n-1
*  Worst case: 1 + (n-2) * 2 = 2n-3
*/

P37 recursion + divide and conquer

/*  Max2:  Recursion + divide and conquer */
void max2(int A[], int lo, int hi, int& x1, int& x2) {
	if (lo + 2 == hi) { /*...*/;  return; }   
	if (lo + 3 == hi) {/*...*/; return; }
	int mi = (lo + hi) / 2;
	int x1L, x2L; max2(A, lo, mi, x1L, x2L);
	int x1R, x2R; max2(A, mi, hi, x1R, x2R);
	if (A[x1L] > A[x1R]) {
		x1 = x1L; x2 = (A[x2L] > A[x1R]) ? x2L : x1R;
	}
	else {
		x1 = x1R; x2 = (A[x1L] > A[x2R]) ? x1L : x2R;
	}
}

Algorithm: iteration and recursion
Algorithm strategy: resolve - and - conquer, divide-and-conquer
Analysis: Recurrence trace, Recurrence

Supplementary knowledge points (important):

Recursive basis: it is an ordinary case of recursive function. Only recursive basis, recursion will not continue all the time.

Reduction and treatment:

  • Recursive instances are: 1 instance with scale n, 1 instance with scale n-1, 1 instance with scale n-2,..., N in total

divide and rule

  • Recursive instances are: 1 instance with scale n, 2 instances with scale n/2, 4 instances with scale n/4,..., with a total of 1 + 2 + 4 + 8 +... + n.

P38

Make it work,
make it right,
make it fast.
—Kent Beck

The first two: it can be realized by recursion
fast uses iteration.

int fib(n) { return (2 > n) ? n : fib(n - 1) + fib(n - 2); }
/*T(n) = O(2^n) */

P41
Going up stairs is similar
P42
1. Memory: memoization (tabulate the results of calculated examples for future reference)
2. Dynamic programming (from small to large, from bottom to top): dynamic programming

Reverse the calculation direction: from top-down recursion to bottom-up iteration.

f = 0; g = 1;   // fib(0), fib(1)
while (0 < n--) {
	g = g + f;
	f = g - f;
}
return g;

/* T(n) = O(n), And only O(1) space is required */

Example: longest common subsequence

Subsequence: it is composed of several characters in the sequence according to the original relative order.
P44
Recursive T(n) = O(n^2)

Dynamic programming: T(n) = O(n*m)
1. List all sub questions (hypothetically) in one table
2. Reverse the calculation direction and calculate all items at one time from LCS(A[0], B[0])

P Limited (school online)

Limitations: caching

Why not learn poetry; If you don't learn etiquette, how can you stand
He has given signs of himself which are visible to those who seek, and not to those who do not seek him.

P cyclic shift

Problem: only use O(1) auxiliary space to move the elements in array A[0, n] by k units to the left.

void shift(int* A, int n, int k);
void shift0(int* A, int n, int k)//Move left repeatedly at intervals of 1
{
	while (k--)  shift(A, n, 0, 1);    
}// K iterations in total, O(n*k)

int shift(int* A, int n, int s, int k) {
	int b = A[s]; int i = s, j = (s + k) % n; int mov = 0; //mov recording times
	while (s != j) // Starting from A[s], shift k bits to the left in turn with K as the interval
	{
		A[i] = A[j]; i = j; j = (j + k) % n; mov++;
	}
	A[i] = b; return mov + 1;    //Finally, the starting element is transferred to the corresponding position
}
/* Iterative version */
void shift1(int* A, int n, int k) {
	for (int s = 0, mov = 0; mov < n; s++)
		mov += shift(A, n, s, k);
}// Move K positions and rotate in a circle with k elements apart

P flip

/* Inverted version */
// With the help of the inversion function, the array is rotated to the left by K bits
void shift2(int* A, int n, int k) {
	reverse(A, k);      // O(3k/2)
	reverse(A + k, n - k);   // O(3(n-k)/2)
	reverse(A, n);          // O(3n/2)
}// O(3n)

Continuous access to Cache

  • The data access order shall be as close as possible
Supplementary notes:

1. The Hailstone problem has not been proved so far, that is, no one has proved that the Hailstone(n) process can be terminated for all positive integers.
2. Turing machine is an ideal calculation model, and its paper tape is an infinite paper tape with infinite extension at both ends.
3. Reduction and Governance: it is divided into two subproblems: an ordinary and a reduced scale.

The time complexity of calculating fib(n) recursively by definition is O(2^n)

Chapter 2 vector

P48
Linear sequence: Vector + List
Abstract Data Type(ADT)

Vector:
The types of elements are not limited to basic types.


P50
Ordered vector:
search(a), if any, directly returns the rank of the element; If it does not exist, it is not greater than the rank of the maximum value in the element of element a. If element a is less than all elements, return - 1.

/* Vector template class */
typedef int Rank;   // Rank
#define DEFAULT_ Capability 3 / / default initial capacity
template<typename T> class Vector {//Vector template class
private:  Rank_size; int _capacity; T* _elem;   // Scale, capacity, data area
protected:
	/*  ... Internal function */
public:
	/* ... Constructor*/
	Vector(int c = DEFAULT_CAPACITY) {
		_elem = new T[_capacity = c]; _size = 0;
	}// default
	Vector(T const* A, Rank lo, Rank hi)//Array interval copy
	{
		copyFrom(A, lo, hi);
	}
	Vector(Vector<T> const& V, Rank lo, Rank hi) //Vector interval replication
	{
		copyFrom(V._elem, lo, hi);
	}
	Vector(Vector<T> const& V) {
		copyFrom(V._elem, 0, V._size);     //Vector global copy
	}

	/* ... Destructor*/
	~Vector() { delete[] _elem; }   // Free up internal space

	/* ... Read only interface*/
	/* ... Writable interface*/
	/* ... Traversal interface*/

};

/*********** Copy*******************/
template <typename T> // T is the basic type, or the assignment operator has been overloaded

void Vector<T>::copyFrom(T* const A, Rank lo, Rank hi) {
	_elem = new T[_capacity = 2 * (hi - lo)];   //Allocate space
	_size = 0;    //Scale clearing
	while (lo < hi)
		_elem[_size++] = A[lo++];    //Copy to_ elem[0, hi-lo]
}

P53 extensible vector

Static space management
Insufficient:

  • Overflow
  • Underflow: there are few elements in a vector. Filling factor (_size / _capacity) < 50%

P54 dynamic space management
Cicada: when you grow up, shed your skin first

template <typename T>
void Vector<T>::expand() {// Capacity expansion when vector space is insufficient
	if (_size < _capacity) return;          //It's not full yet, so there's no need to expand the capacity
	_capacity = max(_capacity, DEFAULT_CAPACITY);   //Not less than the minimum capacity
	T* oldElem = _elem; _elem = new T[_capacity <<= 1];  // Double capacity
	for (int i = 0; i < _size; i++)// Copy original vector content
		_elem[i] = oldElem[i];     //T is a basic type, or the assignment operator '=' has been overloaded
	delete[] oldElem;   //Release the original space
}
/* The vector is encapsulated, and there will be no wild pointer*/

P55
Reasons for adopting the "capacity doubling" strategy:
Other strategies, too many expansion times, increase the time cost.

Blue: increasing; Purple ": double (sacrificing space for time)

P57 allocated complexity

P59

Element access

  • V.get( r )
  • V.put(r, e)
  • A[r]
/* Overloaded subscript operator "[]"*/
template <typename T> // 0 <= r <= _size
T& Vector<T>:: operator[](Rank r)  const { return _elem[r]; }

P60

/*  Vector    Insert operation */
template <typename T> // e is inserted as an element with rank r, 0 < = R < = size
Rank Vector<T>::insert(Rank r, T const& e) {
	expand();  // If necessary, expand the capacity
	for (int i = _size; i > r; i--)   // From back to front
		_elem[i] = _elem[i - 1]    // Subsequent elements move backward one cell in sequence.
		_elem[r] = e; _size++;   // Insert new elements and update capacity
	return r;   // Return rank
}

P61

/* Vector:  Interval deletion */
template <typename T> // Delete interval [lo, HI), 0 < = Lo < = hi < = size
int Vector<T>::remove(Rank lo, Rank hi) {
	if (lo == hi)  return 0;     // Degradation is treated separately for efficiency reasons
	while (hi < _size)  _elem[lo++] = _elem[hi++];  // [hi, _size) move the hi Lo bit forward in sequence
	_size = lo; shrink();    // Update the scale and reduce the volume if necessary
	return hi - lo;
}

P62 single element deletion

/* Single element deletion */
template  <typename T >// Delete the element with rank r in the vector, 0 < = R < = size
T Vector<T>::remove(Rank r) {
	T e = _elem[r]; // Backup deleted elements
	remove(r, r + 1);   // Call interval deletion method
	return e;     // Returns the deleted element
}

P63 find

/*Vector operations: finding */
template <typename T>
Rank Vector<T>::find(T const& e, Rank lo, Rank hi) const {
	while ((lo < hi--) && e != _elem[hi]);   // Reverse search
	return hi;
}

Input sensitivity: best O(1), worst O(n)

P64

/* Vector Operation: weight removal */
template <typename T>
int Vector<T>::deduplicate() {
	int oldSize = _size;
	Rank i = 1;
	while (i < _size)   // Examine the elements one by one from front to back_ elem[i]
		(find(_elem[i], 0, i) < 0) ?// Find the same person in the prefix
		i++
		: remove(i);
	return oldSize - _size;    //  Total number of elements deleted
}
/* O(n^2)*/

Optimization ideas:
1. Following the idea of the efficient version of unify (), the number of element moves can be reduced to O(n), but the comparison number is still O(n^2); Moreover, the stability is destroyed
2. Mark the duplicate elements to be deleted first, and then delete them uniformly. The stability is maintained, but the search length is longer, resulting in more comparison operations.
3,V.sort().uniquify(), O(nlogn)

P65

/* Vector Operations: traversal */
/* Method 1: use the function pointer mechanism to read only or modify locally*/
template <typename T>
void Vector<T>::traverse(void (*visit)(T&))   //Function pointer
{
	for (int i = 0; i < _size; i++)   visit(_elem[i]);
}

/* Method 2: use the function object mechanism to modify globally */
template <typename T>template <typename VST>
void Vector<T>::traverse(VST& visit)// Function object
{
	for (int i = 0; i < _size; i++) visit(_elem[i]);
}
/* Implement a class that adds one to a single T-type element */
template <typename T>
struct Increase {// Function object: implemented by overloading the operator "()"
	virtual void operator()(T& e) { e++; }
};

template <typename T> void increase(Vector<T>& V) {
	V.traverse(Increase<T>());   // Ergodic vector
}

P66:

In an ordered sequence, the order of any pair of adjacent elements
In an unordered sequence, there is always a pair of adjacent elements in reverse order

template <typename T>// Returns the total number of adjacent elements in reverse order
int Vector<T>::disordered()  const {
	int n = 0;
	for (int i = 1; i < _size; i++)   // Check each pair of adjacent elements one by one
		n += (_elem[i - 1] > _elem[i]);   // Count in reverse order
	return n;
}

P67 ordered weight removal

  • Each interval only needs to retain a single element
/*  Vector:  Order + weight removal */
template <typename T> int Vector<T>::uniquify() {
	int oldSize = _size; int i = 0;
	while (i < _size - 1)// Go from front to back and compare the adjacent elements one by one
		(_elem[i] == _elem[i + 1]) ? remove(i + 1):i++;  // If the same, delete the latter; Otherwise, go to the next element
	return oldSize - _size;    // Total number of elements deleted
}
/* Low efficiency: O(n^2) */

P69

  • Delete the deleted elements as a whole and delete them in batches
/* Method 2: order + de duplication */
template <typename T> int Vector<T>::uniquify() {
	Rank i = 0; j = 0;
	while (++j < _size) // Scan one by one until the last element
		if (_elem[i] != _elem[j])
			_elem[++i] = _elem[j];
	_size = ++i; shrink();    // Directly cut off the redundant elements in the tail
	return j - i;  // Total number of deleted elements
}
/* O(n) */


Ordered vector, binary search

/* Unified interface */
template <typename T>// Search algorithm unified interface 0 < = Lo < hi<=_ size
Rank Vector<T>::search(T const& e, Rank lo, Rank hi) const {
	return (rand() % 2) ?
		binSearch(_elem, e, lo, hi)   // Binary search algorithm
		: fibSearch(_elem, e, lo, hi);   // Fibonacci search algorithm
}

P73

template<typename T> // Find elements in ordered vector interval [lo, hi]
static Rank binSearch(T* A, T const& e, Rank lo, Rank hi) {
	while (lo < hi) {
		Rank(mi = (lo + hi) >> 1);
		if (e < A[mi]) hi = mi;   // Small tips: use the less than sign, which is the same as the actual position for easy understanding
		else if (A[mi] < e)  lo = mi + 1;
		else   return mi;
	}
	return -1;    // Search failed
}

P77

template <typename T>
static Rank fibSearch(T* A, T const& e, Rank lo, Rank hi) {
	Fib fib(hi - lo);
	while (lo < hi) {
		while (hi - lo < fib.get()) fib.prev();   
		Rank  mi = lo + fib.get() - 1;  // Golden ratio
		if (e < A[mi]) hi = mi;
		else if (A[mi] < e)  lo = mi + 1;
		else    return mi;
	}
	return -1;
}

P83

template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi) {
	while (1 < hi - lo) {// The algorithm will not terminate until the width of the effective search interval is reduced to 1
		Rank mi = (lo + hi) >> 1;
		(e < A[mi]) ? hi = mi : lo = mi;  //[lo, mi) or [MI, HI) / * MI is included in the right section*/
	}// At exit, hi = lo + 1, and the search interval contains only one element A[lo]
	return (e == A[lo]) ? lo : -1;    // Returns the rank of the hit element or - 1
}

P84

/* Version C */
template <typename T> static Rank binSearch(T* A, T const& e, Rank lo, Rank hi) {
	while (lo < hi) {// A[0, lo) <= e < A[hi, n)
		Rank mi = (lo + hi) >> 1;
		(e < A[mi]) ? hi = mi : lo = mi + 1;   // [lo,mi) or (mi, hi)
	}// At exit, A[lo=hi] is the largest element greater than e
	return --lo;   // Therefore, lo-1 is the maximum rank of elements not greater than e
}

Correctness: invariance + monotonicity

  • Invariance: a [0, Lo) < = e < a [Hi, n)

P87: ordered, interpolation search

  • Interpolation Search

Worst case: O(n), not satisfying uniform independent distribution
Best case: a little test is medium, and the first test is medium.
Average situation: after each comparison, n is reduced to n^0.5
P90
It is applicable to the case where the search interval is very large or the comparison operation cost is very high.

Feasible methods:
Firstly, the search range is reduced to a certain range through interpolation search, and then binary search is carried out.

Large scale: interpolation lookup
Medium scale: half search
Small scale: sequential lookup

If the vector elements are arranged in order, the computational efficiency will be greatly improved

/* Sorter: unified interface*/
template <typename T>
void Vector<T>::sort(Rank lo, Rank hi) {// Interval [lo,hi)
	switch (rand() % 5) {
	case 1: bubbleSort(lo, hi); break;   // Bubble sorting
	case 2: selectionSort(lo, hi);   break;   // Select sort
	case 3: mergeSort(lo, hi); break; // Merge sort
	case 4: heapSort(lo, hi);   break;   // Heap sort
	default: quickSort(lo, hi); break;   // Quick sort
	}
}

bubble sort

For each scan exchange, record whether there are elements in reverse order. If it exists, if and only as over exchange.

template <typename T> void Vector<T>::bubbleSort(Rank lo, Rank hi)
{
	while (!bubble(lo, hi--));      // Scan and exchange one by one until the sequence is complete
}

/* improvement */
template <typename T> bool Vector<T>::bubble(Rank lo, Rank hi) {
	bool sorted = true;    // Overall order sign
	while(++lo < hi)// From left to right, check each pair of adjacent elements one by one
		if (_elem[lo - 1] > _elem[lo]) {//If in reverse order, then
			sorted = false;    //It means that it has not been ordered as a whole and needs to be improved
			swap(_elem[lo - 1], _elem[lo]);   // exchange
		}
	return sorted;   
}

P94

/* Further improvement */
template <typename T> Rank Vector<T>::bubble(Rank lo, Rank hi) {
	Rank last= lo;    // The rightmost reverse pair is initialized to [lo-1, lo]
	while(++lo < hi)// From left to right, check each pair of adjacent elements one by one
		if (_elem[lo - 1] > _elem[lo]) {//If in reverse order, then
			last = lo;    //Update the position record of the rightmost reverse sequence pair, and
			swap(_elem[lo - 1], _elem[lo]);   // exchange
		}
	return last;// Returns the rightmost reverse pair position   
}
/* T(n) = O(n)  */ 

Stability of algorithm

Whether the relative order of repeating elements in the input and output sequence remains unchanged.

  • Bubble sorting is stable
  • Worst case O(n^2)

P97 merge sort

template<typename T>
void Vector<T>::mergeSort(Rank lo, Rank hi) {// [lo, hi)
	if (hi - lo < 2)   return;    // Natural order of unit interval
	int mi = (lo + hi) >> 1;
	mergeSort(lo, mi);   // Sort the first half
	mergeSort(mi, hi);   // Sort the second half
	merge(lo, mi, hi);     // Merge
}

/* Two way merging  */
/* Merge two ordered sequences into one ordered sequence:
* S[lo, hi) = S[lo, mi) + S[mi, hi)
*/

template<typename T> void Vector<T>::merge(Rank lo, Rank mi, Rank hi) {
	T* A = _elem + lo;   // Combined vector a [0, hi LO) = _elem [lo, HI)
	int lb = mi - lo; T* B = new T[lb];  // Front sub quantity B[0, lb) = _elem[lo, mi)
	for (Rank i = 0; i < lb; B[i] = A[i++]);    // Sub vector B before copy
	int lc = hi - mi; T* C = _elem + mi;    // Latter sub vector C [0, LC] =_ elem[mi, hi)
	for (Rank i = 0, j = 0, k = 0; (j < lb) || (k < lc)) {// B[j] and C[k] go to the end of A
		if ((j < lb) && (lc <= k || (B[j] <= C[k]))) A[i++] = B[j++];   // C[k] is no longer or not small  
		if ((k < lc) && (lb <= j || (C[k] < B[j])))    A[i++] = C[k++];   // B[j] no more or greater
	}
	delete[] B;   // Release temporary space B
}
/* The second half element C does not need to be newly opened */
/* Short circuit evaluation: (LC < = k | (B [J] < = C [k]))*/
/* Improvement: if B completes traversal in advance, the program can be terminated directly*/
for (Rank i = 0, j = 0, k = 0; j < lb) {
	if ((k < lc) && (C[k] < B[j]))   A[i++] = C[k++];
	if (lc <= k || (B[j] <= C[k]))   A[i++] = B[j++];
} // Exchange the order of two sentences in the loop and delete the redundant logic

P bitmap (school online)


Small collection + big data
duplicate removal
screen

P supplementary notes

  • The space complexity of recursive version is equal to the maximum recursive depth

2. The result of allocating complexity is lower than the average complexity( ×): The results of apportionment complexity and average complexity are not necessarily related.

3. The return value of the disordered() algorithm is the number of adjacent pairs in reverse order.
4. If the distribution of elements in the (ordered) vector satisfies the independent uniform distribution (before sorting), the average time complexity of interpolation search is O(loglog(n))

Chapter III list

P103-P107

Don't lose the link.
—Robin Milner

Precursor: predecessor
successor: success
First node: first/front
End node: last/rear

Vector: call by rank
List: call by position

/* List node: ListNode template class */
#Define posi (T) listnode < T > * / / list node location

template<typename T>
struct ListNode {// List node template class (implemented in the form of two-way linked list)
	T data;   // numerical value
	Posi(T) pred;    // precursor
	Posi(T) succ;    // Successor
	ListNode() {}   // Construction for header and tracker
	ListNode(T e, Posi(T) p = NULL, Posi(T) s = NULL)
		: data(e), pred(p), succ(s) {} // default constructor 
	Posi(T)  insertAsPred(T const& e); // Front insertion
	Posi(T) insertAsSucc(T const& e);  // Post insert
};

/* List: list template class*/
#include "listNode.h" / / introduce the list node class

template<typename T> class List {// List template class
private: int _size;  // scale
	   Posi(T)  header;  Posi(T)  trailer;  // Head and tail sentry
protected:/* ...Internal function*/
public:      /* ...Constructor, destructor, read-only interface, writable interface, traversal interface*/
};

  • The rank of head, head, end and tail nodes can be understood as - 1, 0, n-1, n respectively
/* structure */

template <typename T> void List<T>::init() {// Initialization, which is called uniformly when creating list objects
	header = new ListNode<T>;   // Create head sentry node
	trailer = new ListNode<T>;  // Create tail sentinel node
	header->succ = trailer;  header-> pred = NULL:  
	trailer->pred = header;  trailer->succ = NULL;   // interconnection
	_size = 0;   // scale
}

-P111 unordered list

P107 rank based access list
Overloaded subscript operator

template <typename T>
T List<T>::operator[](Rank r) const {
	Posi(T) p = first();
	while (0 < r--) p = p->succ;    // The r-th node in sequence is]
	return p->data;  // Target node
}// The rank of any node, that is, the total number of its precursors

P108 find

/* Among the n (true) precursors of node P (possibly trailer), find the last one equal to e*/

template<typename T>// When calling from outside, 0 < = n < = rank (P) <_ size
Posi(T) List<T>::find(T const& e, int n, Posi(T) p) const {//Sequential search
	while (0 < n--) // From right to left, compare the precursors of p with e one by one
		if (e == (p = p->pred)->data)  return p;    // Until hit or range out of bounds

	return NULL;  
}

P109

/* insert */
template<typename T> Posi(T) List<T>::insertBefore(Posi(T) p, T const& e)
{
	_size++; return p->insertAsPred(e);   // e is inserted as the precursor of p
}

template<typename T> // Pre insertion algorithm
Posi(T) ListNode<T>::insertAsPred(T const& e) {
	Posi(T) x = new ListNode(e, pred, this);
	pred->succ = x; pred = x; return x;   // Establish a link and return the location of the new node
}
/* Replication based construction */
template <typename T> // Basic interface
void List<T>::copyNodes(Posi(T) p, int n) {
	init();   // Create and initialize the head and tail sentry nodes
	while (n--) // Insert the n items from p as the end node in turn
	{
		insertAsLast(p->data); p = p->succ;
	}
} 

/* insertBefore(trailer)*/

P110 delete

/* delete */
template <typename T>
T List<T>::remove(Posi(T) p) { // O(1)
	T e = p->data;   // Backup the value of the node to be deleted, which is used to return
	p->pred->succ = p->succ;
	p->succ->pred = p->pred; 
	delete p; _size--; return e;   // Return backup value
}
/*  Delete list */

// Deconstruction
template <typename T> List<T>::~List()  // List destructor
{
	clear(); delete header; delete trailer;  // Clear the list and release the head and tail sentry nodes
}

template<typename T> int List::clear() {// clear list 
	int oldSize = _size;
	where(0 < _size)// Delete the first node repeatedly until the list becomes empty
		remove(header->succ);
	return oldSize;
}// O(n), linearly proportional to the list size

P111 de duplication (uniqueness)

/* Uniqueness */
template <typename T> int List<T>::deduplicate() {// Delete duplicate nodes in unordered list
	if (_size < 2) return 0; 
	int oldSize = _size;
	Posi(T) p = first(); Rank r = 1;  // p starts from the first node
	while (trailer != (p = p->succ)) {//
		Posi(T) q = find(p->data, r, p); // In the first r (true) precursors of p, find the same one
		q ? remove(q): r++;   // If it does exist, delete it, otherwise the rank increases
	}
	return oldSize - _size; // Total number of elements deleted
}

-P114 has a sequence table
P112

/*  Ordered lists: uniqueness*/
template<typename T>int List<T>::uniquify() {// Eliminate duplicate elements in batches
	if (_size < 2)    return 0;     
	int oldSize = _size;   //Record the original scale
	ListNodePosi(T) p = first();  ListNodePosi(T) q;    // p is the starting point of each section and q is its successor
	while (trailer != (q = p->succ))  // Repeatedly examine the adjacent node pairs (p, q)
		if (p->data != q->data)  p = q;   // If they are different, turn to the next section
		else   remove(q);     // Otherwise, delete the latter
	return oldSize - _size;   // Total number of deleted elements
}//  Just traverse the whole list once, O(n)

P114

/*  Ordered lists: finding */
template <typename T> // Among the n (true) precursors with node p in the sequence table, find the last one not greater than e
Posi(T) List<T>::search(T const& e, int n, Posi(T) p) const {
	while (0 <= n--)  // For the nearest n precursors of p, from right to left
		if (((p = p->pred)->data) <= e)   break;   //Compare one by one
	return  p;   // Return to the location where the search ends until the hit, value or range is out of range
}// Best O(1), worst O(n)

RAM: rank based access
Turing machine ™: call-by-position

List sorting algorithm
-P120 select sort

  • Select the largest
  • Bubblesort also has a similar idea. The essence of scanning and exchange is to find the biggest. Worst O(n^2)
/* Select and sort n consecutive elements starting from position p in the list, valid (p) & & rank (p) + n < = size*/
template <typename T> void List<T>::selectionSort(Posi(T) p, int n) {
	Posi(T) head = p->pred; Posi(T)  tail = p;   // Range to be sorted (head, tail)
	for (int i = 0; i < n; i++)  tail = tail->succ;  // head/tail may be a head/tail sentry
	while (1 < n) { // Repeatedly find the largest one from the interval to be sorted and move it to the front of the ordered interval
		insertBefore(tail, remove(selectMax(head->succ, n)));
		tail = tail->pred; n--;   // The range of the to be sorted interval and the ordered interval are updated synchronously
	}
}

new and delete are 100 times the actual value and should be avoided as far as possible

Improvement idea: when the element is the precursor of the last element, there is no need to move. But in fact, the probability of this situation is very low, and such improvement is not worth the loss.

template<typename T>  // Select the largest one from the n elements starting at position p, 1 < n
Posi(T) List<T>::seletMax(Posi(T) p, int n) {
	Posi(T) max = p;   // The maximum value is tentatively determined as p
	for (Posi(T) cur = p; 1 < n; n--)// Subsequent nodes are compared with max one by one
		if (!lt((cur = cur->succ)->data, max->data))   // If > = max / *! lt:   not less than .  Comparator*/
			max = cur;    //  Update the maximum element position record / * take the equal sign to take the later one when there are duplicate elements*/ 
	return max;     // Return maximum node position
}

Improvement: the number of element moves is far less than bubble sorting


-P128 insert sort

1. Search of ordered sequence
2. Insertion of ordered sequence

/* Insert and sort n consecutive elements starting from position p in the list, valid (p) & & rank (p) + n < = size*/
template<typename T> void List<T>::insertionSort(Posi(T) p, int n) {
	for (int r = 0; r < n; r++) {// Each node is introduced one by one, and S(r+1) is obtained from Sr
		insertAfter(search(p->data, r, p), p->data);  // Find + insert
		p = p->succ;   remove(p->pred);      // Turn to the next node
	}// n iterations, each time O(r+1)
}  // Only O(1) auxiliary space is used, which belongs to in place algorithm

Best case: one comparison, 0 exchanges, O(n)
Worst case: each insertion is smaller than the row, O(n^2)

  • Select sorting, the best and worst are O(n^2)

P127
The average time complexity of insertion sorting is O(n^2), which means that although the best case is O(n), the probability of this situation is very low.

P128 reverse sequence pair

  • Input sensitive: insert sort, Hill sort

P129 exercise: LightHouse
divide and rule
Calculate inverse logarithm

Supplementary notes

1. For a two-way list, you can directly access p - > PRED to locate its direct precursor, which only takes O(1) time. For a one-way list, locating its direct precursor needs to access each node one by one from the first node. In the worst case, it needs to traverse the whole list, so it takes O(n) time.

2. The list with length n is equally divided into n/k segments, and the length of each segment is k. there is no reverse order of elements between different segments. The worst time complexity of inserting and sorting the list is O(nk)

Chapter 4 stack and queue

P130

A stack ADT and its implementation

-P132
Stack: a stack of chairs or plates

  • Left stack top
  • LIFO: last in first out
  • Stack is a special case of sequence, which can be derived directly from vector or list
template<typename T> class Stack : public Vector<T> {//Derived from vector
public:  // size(), empty() and other open interfaces can be used directly
	void push(T const& e) { insert(size(), e);  }// Push 
	T pop() { return remove(size() - 1);  }// Out of stack
	T& top() { return (*this)[size() - 1];  }// Take the top
};// Take the beginning / end of the vector as the bottom / top of the stack

C-ary conversion

-P159

/* Binary conversion */
void convert(Stack<char>& S, __int64 n, int base) {
	static char digit[] = //The digit symbols under the new base system can be filled in appropriately according to the value range of base
	{ '0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
	while (n > 0) {// From low to high, calculate the digits under the new base one by one
		S.push(digit[n % base]);   // Remainder stack
		n /= base;    // n is updated to its division of base
	}
}

main() {
	Stack<char> S; convert(S, n, base);   // Use the stack to record the converted digits
	while (!S.empty())  printf("%c", S.pop());   //Reverse order output
}

D bracket matching

-P141

  • Eliminate a pair of adjacent left and right parentheses

Idea:
Sequential scan expression, using the stack to record the scanned part

  • Repeated iteration: in case of (, enter the stack; in case of), exit the stack
/* parenthesis matching */
bool paren(const char exp[], int lo, int hi) {// exp[lo, hi)
	Stack<char> S;   // Use the stack to record the found but unmatched left parenthesis
	for (int i = lo; i < hi; i++)// Check the current characters one by one
		if ('(' == exp[i])    S.push(exp[i]);    // In case of left parenthesis: stack
		else if (!S.empty())  S.pop();    // In case of right parenthesis: if the stack is not empty, the left parenthesis will pop up
		else  return false;     // In case of right parenthesis, the stack is empty and must not match

	return S.empty();    // 
}

Why not use a counter? In order to be easily extended to the coexistence of multiple parentheses.

Stack permutation

-P146

  • Stack A containing elements, transition stack B, and stack B storing results

Different combinations of pop() and push() will get different results

Illegal stack shuffling:
S is empty before each S.pop(); Or the element to pop up is in s, but it is not the top element

Stack shuffling number

F infix expression

-P155
Stack + linear scan

/* Operation expression calculation */
float evaluate(char* S, char* RPN) {// Infix expression evaluation
	Stack<float> opnd, Stack<char> optr;  // Operand stack, operator stack
	optr.push('\0');    // The tail sentry '\ 0' also enters the stack first as the head sentry
	while (!optr.empty()) {// Process each character one by one until the operator stack is empty
		if (isdigit(*S)) // If the current character is an operand, then
			readNumber(S, opnd);   // Read in (possibly multi bit) operand
		else // If the current character is an operator, the priority between it and the operator at the top of the stack will be considered
			switch (orderBetween(optr.top(), *S)) { /* handle separately */
			}
	}// while
	return opnd.pop();    // Pop up and return the final calculation result
}

/* Implementation priority table */
const char pri[N_OPTR][N_OPTR]{// Operator priority
};

switch (orderBetween(optr.top(), *S)) {
case '<':  // Lower stack top priority
	optr.push(*S); S++; break;  // The calculation is delayed, and the current operator is put on the stack
case '=':     // Equal priority (the current operator is a closing parenthesis or a trailing sentinel '\ 0')
	optr.pop(); S++; break;      // Remove parentheses and receive the next character
case '>': { // The operator at the top of the stack has higher priority. The corresponding calculation is implemented and the results are put on the stack
	char op = optr.pop();    // The stack top operator exits the stack and performs the corresponding operation
	if ('!' == op)   opnd.push(calcu(op, opnd.pop()));   // Univariate calculation
	else { float p0pnd2 = opnd.pop(), p0pnd1 = opnd.pop();  // Binary operator
	opnd.push(calcu(p0pnd1, op, p0pnd2));   // Implement the calculation and put the results on the stack
	}
	break;
}

}

P152 - P155 operation example of the above code

G inverse Polish notation (RPN: Reverse Polish Notation)

-P159
Which operator appears first is calculated first

If you want to take it, you must give it first

Infix to postfix:

1. Priority is shown in parentheses
2. Move the operation symbol after the corresponding closing bracket
3. Erase all brackets

/* infix To postfix: conversion algorithm*/
float evaluate(char* S, char*& RPN) {// RPN conversion
	while (!optr.empty()) {//Process each character one by one until the operator stack is empty
		if (isdigit(*S)) // If the current character is an operand
		{ readNumber(S, opnd); append(RPN, opnd.top()); }  // Access RPN
		else// If the current character is an operator
			switch (orderBetween(optr.top(), *S)) {
			case '>': {
				char op = optr.pop(); append(RPN, op);  //Access RPN
			}
			}
	}
}

Queue ADT and its implementation

Badminton bucket, line up

  • One end out, one end in
  • be limited to:

Tail insertion: enqueue() + rear()
Delete header (front): dequeue() + front()

  • FIFO: first in first out

Stack symmetry
The head of the team is on the right
Queues are special cases of sequences and can be derived from vectors or lists

/* queue */
template <typename T>class Queue : public List<T> {//Queue template class derived from list
public: // size() and empty() are used directly
	void enqueue(T const& e) { insertAsLast(e);  }// Join the team
	T dequeue() { return remove(first()); }  // Out of the team
	T& front() { return first()->data;  }   // Team leader
};  // Take the beginning / end of the list as the head / end of the queue
Supplementary notes:

Exercise:



Select the "D" option???

Exercise 2:



Select A???

Chapter 5 binary tree

A tree

-P169

Tree: a list of lists. semilinear

  • Hierarchical relationship

vertex , edge
A tree is a special graph T= (V, E), the number of nodes | V| = n, and the number of edges | E| = e

Child
Sibling
Father
Degree

P167:
access
Path length: number of sides

Connected graph
Acyclic graph

The height of the empty tree is taken as - 1

Representation of B-tree

P170

C binary tree

P175

D binary tree implementation

P178

/* BinNode template class */

#Define binnodeposi (T) binnode < T > * / / node location

template <typename T> struct BinNode {
	BinNodePosi(T)  parent, lChild, rChild;  // Father, child
	T data; int height; int size();   // Height and subtree size
	BinNodePosi(T) insertAsLC(T const &);   // Insert new node as left child
	BinNodePosi(T)  insertAsRC(T const&);   // Insert new node as right child
	BinNodePosi(T)  succ();    //Direct successor of the current node (in the sense of middle order traversal)
	template <typename  VST > void travLevel(VST &);  // Hierarchical traversal of subtree
	template <typename  VST > void travPre(VST &);    // Subtree preorder traversal
	template <typename VST > void travIn(VST &);   // Order traversal in subtree
	template <typename VST > void travPost(VST&);   // Postorder traversal of subtree
};
/* BinNode Interface implementation */

template <typename T > BinNodePosi(T) BinNode<T>::insertAsLC(T const& e)
{
	return lChild = new BinNode(e, this);
}// O(1)

template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC(T const& e)
{
	return rChild = new BinNode(e, this); 
}  // O(1)

template <typename T>
int BinNode<T>::size() { // The total number of offspring, that is, the size of the subtree with which it is rooted
	int s = 1;   // Count into itself
	if (lChild)  s += lChild->size();   // Recursive inclusion of left subtree size
	if (rChild)  s += rChild->size();   // Recursive right subtree size

	return s;
}// O(n)
/* BinTree template class */

template <typename T> class BinTree {
protected:
	int _size;   // scale
	BinNodePosi(T) _root;  // Root node
	virtual int updateHeight(BinNodePosi(T) x);   // Update the height of node x
	void updateHeightAbove(BinNodePosi(T) x);    // Update the height of x and ancestors

public:
	int size() const { return _size;  }   // scale
	bool empty() const { return !_root;   } // Air judgment
	BinNodePosi(T)  root() const { return _root;   }// tree root
	/* ... Subtree interface, delete and detach interface*/
	/* ... Traverse interface*/
};
/* Height update */

# define stature(p) ((p)?  (p) - > height: - 1) / / node height --- the agreed empty tree height is - 1

template <typename T> // Update the node x height. The specific rules vary from tree to tree
int BinTree<T>::updateHeight(BinNodePosi(T) x) {
	return x->Height = 1 +
		max(stature(x->lChild), stature(x->rChild));
}// The conventional binary tree rule, O(1), is adopted here

template <typename T> // Height of renewal v and its ancestors
void BinTree<T>::updateHeightAbove(BinNodePosi(T) x) {
	while (x)  // Can be optimized: once the height is surrounded, it can be terminated
	{
		updateHeight(x);   x = x->parent;
	}
}// O(n = depth(x))
/* Node insertion */

template <typename T> BinNodePosi(T)
BinTree<T>::insertAsRC(BinNodePosi(T) x, T const& e) {// insertAsLC symmetry
	_size++;  x->insertAsRC(e);   // The height of ancestors may increase, and the other nodes must remain unchanged
	updateHeightAbove(x);   
	return x->rChild;
}

E-preorder traversal

P183

/* Preorder traversal_ recursion*/
template <typename T, typename VST>
void traverse(BinNodePosi(T) x, VST& visit) {
	if (!x) return;
	visit(x->data);
	traverse(x->lChild, visit);
	traverse(x->rChild, visit);
}// T(n) = O(n)
/* Preorder traversal_ Iteration 1*/
template <typename T, typename VST>
void travPre_I1(BinNodePosi(T) x, VST& visit) {
	Stack <BinNodePosi(T)> S;   // Auxiliary stack
	if (x) S.push(x);   // Root node stack
	while (!S.empty()) {// Loop repeatedly before the stack becomes empty
		x = S.pop(); visit(x->data);   // Pop up and access the current node
		if (HasRChild(*x))  S.push(x->rChild);   // Right child first in and then out
		if (HasLChild(*x))  S.push(x->lChild);   //Left child rear in first out
	}
}
/* Preorder traversal_ Iteration 2*/
template <typename T, typename VST>// Apportionment O(1)
static void visitAlongLeftBranch(
	BinNodePosi(T) x,
	VST& visit,
	Stack <BinNodePosi(T)>& S) {
	while (x) {// Repeatedly
		visit(x->data);   // Access current node
		S.push(x->rChild);  // Right child (right subtree) into the stack (out of the stack in reverse order in the future)
		x = x->lChild;  // Down the left chain
	}
}

/* Main algorithm */
void travPre_I2(BinNodePosi(T) x, VST& visit) {
	Stack <BinNodePosi(T)> S; // Auxiliary stack
	while (true) {//Access nodes batch by batch in the unit of (right) subtree
		visitAlongLeftBranch(x, visit, S); // Access the left chain of subtree x and put the right subtree into the stack buffer
		if (S.empty()) break;   // Exit when stack is empty
		x = S.pop();    // Pop up the root of the next subtree
	}
}

The order of first order traversal is: first access the nodes on the left chain from top to bottom, and then access their right subtree from bottom to top.

Inorder traversal in F

P192

/* Middle order traversal_ recursion*/
template <typename T, typename VST>
void traverse(BinNodePosi(T) x, VST& visit) {
	if (!x) return;
	traverse(x->lChild, visit);
	visit(x->data);
	traverse(x->rChild, visit);
}

/* Middle order traversal_ iteration */

/* Start from the root node and humiliate the left chain of control until a node has no left node (the point of access)*/
/* Access the left chain node -- > traverse the right subtree (bottom-up)*/

template <typename T>
static void goAlongLeftBranch(BinNodePosi(T) x, Stack <BinNodePosi(T)>& S)
{
	while (x) { S.push(x); x = x->lChild;  }
}// Repeatedly stack and go deep along the left branch
template <typename T, typename V> void  travIn_I1(BinNodePosi(T) x, V& visit) {
	Stack <BinNodePosi(T)> S;   // Auxiliary stack
	while (true) {// Repeatedly
		goAlongLeftBranch(x, S);   // Starting from the current node, batch by batch into the stack
		if (S.empty()) break;   // Until all nodes are processed
		x = S.pop();    // The left subtree of x is either empty or traversed (equivalent to empty), so it can
		visit(x->data);    // visit
		x = x->rChild;   // Then turn to its right subtree
	}
}

T= O(n)

G post order traversal (school online)

/* Postorder traversal_ recursion*/
template <typename T, typename VST>
void traverse(BinNodePosi<T> x, VST& visit) {
	if (!x)  return;
	traverse(x->lc, visit);
	traverse(x->rc, visit);
	visit( x-> data)
}
/* Postorder traversal_ iteration*/
template <typename T> static void gotoLeftmostLeaf(Stack <BinNodePosi<T>>& S) {
	while( BinNodePosi<T> x = S.top() )  // Check the top node of stack repeatedly from top to bottom
		if (HasLChild(*x)) {// As far left as possible
			if (HasRChild(*x)) // If there is a right child, then
				S.push(x->rc);    // Priority stack
			S.push(x->lc);    // Then turn to the left child
		}
		else   // It's a last resort
			S.push(x->rc);   // Just turn to the right child
	S.pop();    // Before returning, pop up the empty node at the top of the stack
}

template <typename T, typename VST>
void travPost_I(BinNodePosi<T> x, VST& visit) {
	Stack <BinNodePosi<T>> S;   // Auxiliary stack
	if (x) S.push(x);  // The root node is stacked first
	while (!S.empty()) {// x is always the current node
		if (S.top() != x->parent)  // If the top of the stack is not the father of x (but the right brother)
			gotoLeftmostLeaf(S);   // Find the leftmost leaf in its right subtree (recursion)
		x = S.pop();   // Pop up the top of the stack (i.e. after the previous node) to update x
		visit(x->data);   // Visit immediately
	}
}

H-level traversal

P199

/* level traversal  */
template <typename T> template <typename VST>
void BinNode<T>::travLevel(VST& visit) {// Binary tree hierarchy traversal
	Queue<BinNodePosi(T)> Q;   // Introducing auxiliary queue
	Q.enqueue(this);  // Root node join
	while (!Q.empty()) {// Iterate before the queue becomes empty again
		BinNodePosi(T) x = Q.dequeue();   // Take out the first node of the team, and then
		visit(x->data);  // Visit
		if (HasLChild(*x))  Q.enqueue(x->lChild);  //Left child joins the team
		if (HasRChild(*x))  Q.enqueue(x->rChild);   // Right child joins the team
	}
}

Queue: first in first out

Suddenly no card, began to watch online in the school

I reconstruction

P202
[first order | second order] + middle order
If there is no middle order, it cannot be reconstructed, because the left subtree and right subtree may be empty

J Huffman tree

Code table: protocol
Prefix- Free Code
Avoid excessive depth difference
Large difference in word frequency:
Income = height difference × Frequency difference

Optimize coding strategy:

  • The high frequency should be placed as high as possible, and the low frequency should be placed as low as possible (long)

Supplementary notes:

1. Node range of complete binary tree with height H: [2^(h-1)+1
2^(h+1)-1]
2. For each layer, the depth decreases by 1, but the increase of height may be greater than 1, because the height of the node is determined by the higher of its left and right subtrees.
3. Post order traversal: starting from the root node, for each node in the middle, you can go left to left, but not left to right. If there is no way to go left or right, this node is the first visited node in the post order traversal.
4. Let the binary tree have n nodes with a height of h, insert a new node in it, and the number of nodes whose height changes is O(h).
On the path from the newly inserted node to the root node, the height of all nodes (i.e. the ancestors of the new node) may change.

Chapter VI diagram

A

Terminology, implementation, algorithm

undirected edge
Undirected graph: undigraph
digraph
directed edge
(U, v): tail, u, head

Path: a sequence consisting of a series of vertices that are contiguous in turn

Simple path: a path without duplicate nodes

Directed acyclic graph
Euler loop: pass all sides once, and just once
Hamiltonian loop: pass through each vertex once and exactly once

B adjacency matrix

/* Graph template class*/
template <typename Tv, typename Te> class Graph {//Vertex type, edge type
private:
	void reset() {//Reset auxiliary information of all vertices and edges
		for (int i = 0; i < n; i++) {// vertex
			status(i) = UNDISCOVERED; dTime(i) = fTime(i) = -1;
			parent(i) = -1; priority(i) = INT_MAX;
			for (int j = 0; j < n; j++)// edge
				if (exists(i, j))  status(i, j) = UNDETERMINED;
		}
	}
public:   /* ... Vertex operation, edge operation, graph algorithm*/
}; 


Undirected graph + adjacency matrix: redundancy

/* Vertex */
typedef enum { UNDISCOVERED, DISCOVERED, VISITED}VStatus;

template <typename Tv> struct Vertex {// Vertex object
	Tv data; int inDegree, outDegree; // Data and access degree

	// For graph traversal
	VStatus status;  // (above three states)
	int dTime, fTime; // Time stamp
	int parent;   // Parent node in traversal tree
	int priority;  // Priority in traversal tree (shortest path, very short edge crossing, etc.)

	Vertex(Tv const& d) :  // Construct new vertex
		data(d), inDegree(0), outDegree(0), status(UNDISCOVERED),
		dTime(-1), fTime(-1), parent(-1),
		priority(INT_MAX) {}
};
/* Edge */
typedef enum { UNDETERMINED, TREE,CROSS, FORWARD, BACKWARD}EStatus;

template <typename Te> struct Edge {// Edge object
	Te data;   // data
	int weight;  // weight
	EStatus status;   // type
	Edge(Te const& d, int w) :  // Construct new edge
		data(d), weight(w), status(UNDETERMINED) {}
};
/* GraphMatrix*/
template <typename Tv, typename Te>class GraphMatrix :public Graph<Tv, Te> {
private:
	Vector< Vertex<Tv> > V;  // Vertex set
	Vector< Vector< Edge<Te>* >> E;  // Edge set
public:
	/* Operation interface: vertex dependent, edge dependent*/
	GraphMatrix() { n = e = 0; }// structure
	~GraphMatrix() {// Deconstruction
		for (int j = 0; j < n; j++)
			for (int k = 0; k < n; k++)
				delete E[i][j];  // Clear the edge records of all dynamic applications.
	}
};

/* vertex operations  */
// For any vertex i, enumerate all its adjacent vertices
int nextNbr(int i, int j) {// If it has been enumerated to neighbor j, move to the next neighbor
	while ((-1 < j) && !exists(i, --j));  // Reverse order search, O(n)
	return j;
}// Use adjacency table to increase to o (1 + outdegree (I))

int firstNbr(int i) {
	return nextNbr(i, n);  // n: Hypothetical sentry
}// First neighbor

P214 side operation

/* Side operation */

bool exists(int i, int j) {// Judge whether the edge (i, j) exists
	return (0 <= i) && (i < n) && (0 <= j) && (j < n)   // i. J comparison of legitimacy
		&& E[i][j] != NULL;  // Short-circuit evaluation 
}

/* Edge insertion */
void insert(Te const& edge, int w, int i, int j) {// Insert (i, j, w)
	if (exists(i, j)) return;   // Ignore existing edges
	E[i][j] = new Edge<Te>(edge, w);  // Create new edge
	e++;   // Update edge count
	V[i].outDegree++;   // Update the out degree of the associated vertex i
	V[j].inDegree++;  // Update the penetration of the associated vertex j
}

/* Edge deletion */
Te remove(int i, int j) {// Delete the join edges between vertices i and j (exists(i, j)) 
	Te eBak = edge(i, j);   // Backup information of side (i, j)
	delete E[i][j]; E[i][j] = NULL;  // Delete edges (I, J)

	e--;   // Update edge count
	V[i].outDegree--; // Update the out degree of the associated vertex i
	V[j].inDegree--;   // Update the penetration of the associated vertex j
	return eBak;   //Returns the information of the deleted edge
}
/* Introducing new vertices */
int insert(Tv const& vertex) {// Insert vertex, return number
	for (int j = 0; j < n; j++)
		E[j].insert(NULL); n++;
	E.insert( Vector< Edge<Te>*>(n, n, NULL) );
	return V.insert(Vertex<Tv>(vertex));
}

/* Vertex deletion */
Tv remove(int i) {// Delete the vertex and its associated edge, and return the vertex information
	for(int j=0; j<n; j++)
		if (exists(i, j))// Delete all outgoing edges
		{
			delete E[i][j]; V[j].inDegree--;
		}
	E.remove(i); n--;  // Delete line i
	for(int j=0; j<n; j++)
		if (exists(j, i))// Delete all edges and column i
		{
			delete E[j].remove(i); V[j].outDegree--;
		}
	Tv vBak = vertex(i);  // Backup vertex i information
	V.remove(i);   // Delete vertex i
	return vBak;  // Returns the information of the deleted vertex
}

Plan graph
Meet: v - e + f - c = 1
Plan: e ≤ 3n-6

D. breath first search

P217

simplify what is complicated

Spanning Tree
Hierarchical traversal of tree

/* Graph:  BFS()Breadth first search*//* Breath-First-Search*/
template <typename Tv, typename Te>// Vertex type, edge type
void Graph<Tv, Te>::BFS(int v, int& clock) {
	Queue<int> Q; status(v) = DISCOVERED; Q.enqueue(v);  //initialization
	while ( !Q.empty() ) {// Repeatedly
		int v = Q.dequeue();
		dTime(v) = ++clock; // Take out the top v of the team and
		for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u))//Check every neighbor u of v
			/* ... Depending on the state of u, deal with it separately */
			status(v) = VISITED;   // So far, the current vertex access is completed
	}
}
/* Graph:  BFS()Breadth first search*//* Breath-First-Search*/
template <typename Tv, typename Te>// Vertex type, edge type
void Graph<Tv, Te>::BFS(int v, int& clock) {
	Queue<int> Q; status(v) = DISCOVERED; Q.enqueue(v);  //initialization
	while ( !Q.empty() ) {// Repeatedly
		int v = Q.dequeue();
		dTime(v) = ++clock; // Take out the top v of the team and
		for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u))//Check every neighbor u of v
			/* ... Depending on the state of u, deal with it separately */
			status(v) = VISITED;   // So far, the current vertex access is completed
	}
}

///
while (!Q.empty()) {// Repeatedly
	int v = Q.dequeue();  dTime(v) = ++clock;   // Take out the top v of the team and
	for( int u= firstNbr(v); -1 <u; u=nextNbr(v, u) )// Check every neighbor u of v
		if (UNDISCOVERED == status(u)) {// If u has not been found, then
			status(u) = DISCOVERED; Q.enqueue(u);   // The vertex was found
			status(v, u) = TREE;  parent(u) = v;  // Introduce tree edge
		}
		else// If u has been found (in the queue), or even accessed (out of the queue), then
			status(v, u) = CROSS;   // Classify (v, u) as span edge
	status(v) = VISITED;   // At this point, the current vertex access is completed
}

The continuous, regular and compact organization form is conducive to the role of cache mechanism.

P222 multi connected

/* Multiple connectivity */
/* Graph::bfs() */
template <typename Tv, typename Te>// Vertex type, edge type
void Graph<Tv, Te>::bfs(int s) {// s is the starting point
	reset(); int clock = 0; int v = s;   // initialization
	do // Check all vertices one by one. Once you encounter a vertex that has not been found
		if (UNDISCOVERED == status(v))  // 
			BFS(v, clock);   // That is, start BFS once from this vertex
	while (s != (v = (++v % n)));
	// Access by serial number, so it is not missed or repeated
}

P224 shortest path

E depth first search

P225

Support tree

/* Graph:: DFS() */ /* Depth-First Search , Depth first search */

template Graph<Tv, Te>::DFS(int v, int & clock) {
	dTime(v) = ++clock; status(v) = DISCOVERED;   // Find current vertex v
	for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u))  // Enumerate each neighbor u of v
		/* ... Depending on the state of u, deal with it separately */
		/* ... Unlike BFS, it contains recursion */
		status(v) = VISITED; fTime(v) = ++clock;  // At this point, the current vertex v direction access is completed
}

/*****************/
for( int u = FirstNbr(v); -1< u; u = nextNbr(v, u))  // Enumerate all neighbors of v
	switch (status(u)) { // And treat them separately according to their status
	case UNDISCOVERED: // u has not been found, which means that the support tree can be extended here
		status(v, u) = TREE; parent(u) = v; DFS(u, clock); break;   // recursion
	case DISCOVERED: // u has been discovered but has not been visited. It should belong to the ancestor pointed by future generations
		status(v, u) = BACKWARD; break;
	default: // If u has been VISITED (VISITED, directed graph), it can be divided into forward edge or cross edge according to the inheritance relationship
		status(v, u) = dTime(v) < dTime(u) ? FORWARD : CROSS; break;
	}

Backtrack

P229 directed graph
Edge classification
P231 bracket principle / nesting principle
Supplementary notes:
1. The number of TREE edges is always equal to the number of vertices minus the number of connected components.
2. When DFS is performed on the graph, the BACKWARD edge means that the graph contains loops.

School online:

Zero entry algorithm for F1 topological sorting

Topology sorting:
For any directed graph (not necessarily directed acyclic graph (DAG)), try to arrange all vertices into a linear sequence so that the suborder must be compatible with the original graph

  • In any directed acyclic graph, there must be a point with zero degree.
/* Topological sorting: output zero degree vertices sequentially */
// Store all vertices with zero degree into stack S and empty queue Q

while (!S.empty()) {
	Q.enqueue(v = S.pop());   // Stack top v into queue
	for each edge(v, u)// The adjacent vertex of v, if the penetration is only 1
		if (u.inDegree < 2)   S.push(u);   // Then enter the stack

	G = G\ { v };  // Delete v and its associated edges (adjacency vertex penetration minus 1)
}

return | G | ? "NOT A DAG" : (? );   // Only if the original graph can be topologically sorted

Zero out degree algorithm for F2 topological sorting

  • Begin with the end in mind. (beginning with the end)
  • A real farmer doesn't need to be anxious... He doesn't require all the products to belong to him when he completes his work every day. What he offers is not only his first fruit, but also his last fruit.
    ----From Walden
Supplementary notes

1. In a simple undirected graph with n vertices, the maximum number of edges is: (n-1) × n/2
2. The number of edges of an undirected graph is equal to half of the sum of the degrees of each vertex.
3. A+ D = MM^T: a (adjacency matrix of simple undirected graph G), M (incidence matrix of G), D (diagonal matrix in which the ith element on the diagonal is the degree of vertex i)
4. The space complexity of realizing a graph with n vertices and e edges by adjacency matrix is O(n^2)
5. The time complexity of deleting edge (i, j) is O(1)
6. Time complexity of accessing data stored in vertex v O(1)

Chapter VII application of map (school online)

A1 double connected component: judgment criteria

information cocoons
Joint points (after removal, the number of connected domains increases)
Leaf nodes are not joint points

The highest ancestor

A2 biconnected component decomposition: algorithm

/* Graph:: BCC() */

#Define hca() (fTime (x)) / / use the idle fTime here
template <typename Tv, typename Te>
void Graph<Tv, Te>::BCC(int v, int& clock, Stack<int>& S) {
	hca() = dTime(v) = ++clock;  status(v) = DISCOVERED; S.push(v);
	for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u))
		switch (status(u))
		{  /* ...Depending on the state of u, deal with it separately*/
		case UNDISCOVERED:
			parent(u) = v; type(v, u) = TREE;  // Expand tree edge
			BCC(u, clock, S);  // Start traversal from u and return
			if (hca(u) < dTime(v))// If u points back to the true ancestor of v
				hca(v) = min(hca(v), hca(u));   // Then v the same is true
			else // Otherwise, take v as the joint point (below u is a BCC, and the vertices are concentrated at the top of stack S)
				while (u != S.pop());   // Pop up all nodes in the current BCC (except v)
		                                              // Further treatment as required
			break;
		case DISCOVERED:
			type(v, u) = BACKWARD;
			if (u != parent(v))
				hca(v) = min(hca(v), dTime(u));   // Update hca[v], smaller higher
			break;

		default:
			type(v, u) = dTime(v) < dTime(u) ? FORWARD : CROSS;
			break;
		}
	status(v) = VISITED;   // End of access to v
}
# undef hca

A3 double connected component decomposition: Example

B priority search (BAG)

template <typename Tv, typename Te>
template<typename PU> // Priority updater (function object)

void Graph<Tv, Te>::pfs(int s, PU prioUpdater) {
	priority(s) = 0; status(s) = VISITED; parent(s) = -1;  // The starting point s is added to the PFS tree
	while (1) {// Adds the next vertex and edge to the PFS tree
		/* ... Introduce n-1 vertices (and N-1 edges) in turn*/
		for (int w = firstNbr(s); -1 < w; w = nextNbr(s, w)) // To s each neighbor w
			prioUpdater(this, s, w);  // Updates the priority of vertices w and their parent vertices
		for( int shortest = INT_MAX, w = 0; w < n; w++)
			if( UNDISCOVERED == status(v) ) // From vertices that have not been added to the traversal tree
				if (shortest > priority(w))   // Pick the next one
				{
					shortest = priority(w); s = w;
				}// Highest priority vertex s
		if (VISITED == status(s))  break;   // Until all vertices have been added
		status(s) = VISITED; type(parent(s), s) = TREE; // Add s to the traversal tree
	}//while
}

C Dijkstra algorithm (shortest path method)

Creating tools
Reduce and govern

/* PrioUpdater() */

g->pfs(0, DijkstraPU<char, int>());   // Starting from vertex 0, start Dijkstra algorithm

template <typename Tv, typename Te>struct DijkPU {// Vertex priority updater of Dijkstra algorithm
	virtual void operator() { Graph <Tv, Te>* g, int uk, int v } {//For every uk
		if (UNDISCOVERED != g->status(v))  return;   // Undiscovered neighbors v, press
		if (g->priority(v) > g->priority(uk) + g->weight(uk, v)) {// Dijkstra
			
			g->priority(v) = g->priority(uk) + g->weight(uk, v);
			g->parent(v) = uk;   // Do relaxation

		}
	}

};

D Prim algorithm (minimum spanning tree problem)

Minimum tree covering all nodes in the graph: support tree
Weight cannot be negative

After cutting, the weight will not increase

Incremental construction

The minimum spanning tree is not unique (different starting points, different spanning trees)

Find the shortest one first. Isn't this the shortest overall?

/* PrioUpdater()*/
g->pfs(0, PrimPU<char, int>());  // Starting from the vertex, start the Prim algorithm
template <typename Tv, typename Te>struct PrimPU {// Vertex priority updater of Prim algorithm
	virtual void operator() (Graph<Tv, Te>* g, int uk, int v) {// For every uk
		if (UNDISCOVERED != g->status(v))  return;   // Undiscovered neighbors v, press
		
		if (g->priority(v) > g->weight(uk, v)) {// Prim
			g->priority(v) = g->weight(uk, v);
			g->parent(v) = uk;   // Do relaxation
		}

	}
};

Chapter 8 binary search tree

P232

A

Definition, characteristics and specifications
call - by - key
Key
Comparison and comparison
entry

/* Entry */
template <typename K, typename V> struct Entry {// Entry template class
	K key; V value; // Key and value
	Entry(K k = K(), V v = V())::key(k), value(v) {};   // Default constructor 
	Entry(Entry<K, V> const& e)::key(e.key), value(e.value) {}; // clone

	// Comparator, discriminator
	bool operator< (Entry<K, V> const& e) { return key < e.key;  } //less than
	bool operator> (Entry<K, V> const& e) { return key > e.key; } // greater than
	bool operator == (Entry <K, V> const& e) { return key == e.key; }   // be equal to
	bool operator !=  (Entry <K, V> const& e) { return key != e.key; }   // Not equal to
};

Node ~ entry ~ key
The middle order ergodic sequence of BST must be monotonic and non descending

/* BST template class */
template <typename T> class BST : public BinTree<T> {// Derived from BinTree
public:  // Decorated with virtual to override derived classes
	virtual BinNodePosi(T)& search(const T&);  // lookup
	virtual BinNodePosi(T) insert(const T&); // insert
	virtual bool remove(const T&);   // delete

protected:
	BinNodePosi(T) _hot;  // Father of hit node
	BinNodePosi(T) connect34(// 3 + 4 reconstruction
		BinNodePosi(T), BinNodePosi(T), BinNodePosi(T),
		BinNodePosi(T), BinNodePosi(T), BinNOdePosi(T), BinNOdePosi(T));
	BinNodePosi(T) rotateAt(BinNodePosi(T));  // Rotation adjustment
};

B1 BST: find

P237
Reduce and govern

/* Binary search tree: finding*/
template <typename T> BinNodePosi(T)& BST<T>::search(const T& e)
{
	return searchIn(_root, e, _hot = NULL);  // Start lookup from root node
}

static BinNodePosi(T)& searchIn(// Typical tail recursion can be changed to daddy's version
	BinNodePosi(T)& v,   // Current (sub) tree root
	const T& e,  // Target key
	BinNodePosi(T)& hot)// Memory hotspot
{
	if (!v || (e == v->data)) return v;   // Sufficient to determine success or failure, success, or
	hot = v;    // First write down the current (non empty node), and then
	return searchIn(((e < e->data) ? v->lChild : v->rChild), e, hot);
}// The running time is proportional to the depth of the return node v, which does not exceed the tree height O(h)

Add sentinel node

B2 BST: Insert

P242

/* Binary search trees: inserting*/
template <typename T> BinNodePosi(T) BST<T>::insert(const T& e) {
	BinNodePosi(T)& x = search(e);   // Find target
	if (!x) {// Similar elements are prohibited, so the insertion operation is implemented only when the search fails
		x = new BinNode<T>(e, _hot);  // Create a new node at x to_ hot for father
		_size++; updateHeightAbove(x);   // Update the size of the whole tree, update x and the height of its ancestors
	}
	return x;   // Whether e exists in the original tree or not, there is always X - > data = = E
}

B3 BST: delete

/* Binary search tree: deleting */
template <typename T> bool BST<T>::remove(const T& e) {
	BinNodePosi(T)& x = search(e); // Locate the target node
	if (!x) return false;  // Confirm that the target exists (at this time _hot is the father of x)
	// The element cannot be deleted until it exists
	removeAt(x, _hot);   // Delete and update the size of the whole tree in two categories
	_size--;   // Update full tree size
	updateHeightAbove(_ hot);   // Renew_ hot and the height of its ancestors
	return true;
}// Whether the deletion is successful or not is indicated by the return value
/* Binary search tree deletion*/
/* Case 1: if a subtree of * x is empty, it can be replaced by another subtree  */
template <typename T> static BinNodePosi(T)
removeAt(BinNodePosi(T)& x, BinNodePosi(T)& hot) {
	BinNodePosi(T) w = x;  // The initial value of the node actually removed is the same as x
	BinNodePosi(T) succ = NULL;  // The successor of the actually deleted node
	if (!HasLChild(*x))   succ = x = x->rChild;  // Left subtree is empty
	else if (!HasRChild(*x))  succ = x = x->lChild;  // The right subtree is empty
	else {  /*  ... The coexistence of left and right subtrees*/  // Situation 2
		w = w->succ(); swap(x->data, w->data);  // Make * x exchange data with its successor
		BinNodePosi(T) u = w->parent;  // The original problem is transformed into removing the non quadratic node w
		(u == x ? u->rChild : u->lChild) = succ = w->rChild;	
	}
	hot = w->parent;   // Record the parent of the node actually deleted
	if (succ)   succ->parent = hot;   // Associate the successor of the deleted node with hot
	release(w->data); release(w);   // Release the removed node
	return succ;   // Return to successor
}// O(1)

/* Case 2: */
/*1,Find the minimum point in the right subtree and exchange it to the node to be deleted,
* 2,At this time, you can continue to process the node to be deleted according to the first situation
*/

C balance

P248

Random generation: O(logn)
Random composition: O(n^0.5), more reliable

Ideal balance:

Moderate balance: if it does not exceed O(logn) gradually, it can be called moderate balance.

Moderately balanced BST: balanced binary search tree (BBST)

Middle order ergodic ambiguity

Equivalent BST: variable up and down, no disorder left and right

D1 & 2 AVL tree: rebalance

P253

Balance factor = left subtree height - right subtree height

AVL tree = moderately balanced

/* AVL Interface */
#define Balanced(x) \ / / ideal balance
(  stature ( (x).lChild ) == stature( (x).rChild ) )

#define BalFac(x) \ / / balance factor
( stature( (x).lChild ) - stature ( (x).rChild ) )

#Define avlbalanced (x) \ / / AVL balance condition
( ( -2 < BalFac(x) ) && ( BalFac(x) < 2 ) )

template <typename T> class AVL :public BST<T> {// Derived from BST
public:  // BST::search() and other interfaces can be used directly
	BinNodePosi(T)  insert(const T&);   // Insert override
	bool remove(cosnt T&);   // Delete override
};

Notes:

  • After inserting a node into the AVL tree, the maximum number of unbalanced nodes is O(lgn)
  • After deleting a node in the AVL tree, the maximum number of unbalanced nodes is O(1)

D3 AVL tree: inserting


zagzag
zigzig
O(1)

zigzag
zagzig

/* AVL Trees: inserting*/
template <typename T> BinNodePosi(T) AVL<T>::insert(const T& e) {
	BinNodePosi(T)& x = search(e); if (x) return x;  // If the target does not exist
	x = new BinNode<T>(e, _hot); _size++; BinNodePosi(T) xx = x;  // Create x

	// Starting from the father of x, check the ancestors g of each generation layer by layer
	for( BinNodePosi(T) g = x-> parent; g; g= g->parent)
		if (!AvlBalanced(*g)) {// Once g imbalance is found, the balance is restored by adjustment
			FromParentIn(*g) = rotateAt( tallerChild( tallerChild(g) ) );
			break;    // g after rebalancing, the height of local subtree must be restored; Their ancestors must be the same, so the adjustment is over
		}
		else // Otherwise (before the group that is still balanced)
			updateHeight(g);   // Update its height
	return xx;   // Return to new node: only one adjustment is required at most
}

D4AVL tree: deleting

P261

/* AVL Trees: deleting */
template <typename T> bool AVL<T>::remove(const T& e) {
	BinNodePosi(T)& x = search(e); if (!x)  return false;   // If the target does exist
	removeAt(x, _hot); _size--;   // After deleting according to BST rules_ Both hot and ancestors may be unbalanced

	// From_ Starting from hot, check the ancestors of each generation layer by layer
	for (BinNodePosi(T) g = _hot; g; g = g->parent) {
		if ( !AvlBalanced(*g) ) // Once g imbalance is found, the balance is restored by adjustment
			g = FromParentTo(*g) = rotateAt( tallerChild( tallerChild(g) ) );
		updateHeight(g);   // Update height
	}
	return true;   // Deleted successfully
}

The deletion of nodes in AVL tree causes imbalance, which is rebalanced after rotation adjustment. At this time, the height of subtree containing g, P and V may remain unchanged or decrease by 1.
Correcting the imbalance caused by deleting nodes in AVL tree may lead to imbalance propagation

Correcting the imbalance caused by inserting nodes in AVL tree will not cause imbalance propagation

D5 AVL tree: 3 + 4 reconstruction

P264
Rubik's cube assembly
Middle order traversal

/* 3 + 4 restructure */
template <typename T> BinNodePosi(T) BST<T>::connect34(
	BinNodePosi(T) a, BinNodePosi(T) b, BinNodePosi(T) c,
	BinNodePosi(T) T0, BinNodePosi(T) T1, BinNodePosi(T) T2, BinNodePosi(T) T3)
{
	a->lChild = T0; if (T0)  T0->parent = a;
	a->rChild = T1; if (T1) T1->parent = a;  updateHeight(a);
	c->lChild = T2; if (T2) T2->parent = c;
	c->rChild = T3; if (T3) T3->parent = c; updateHeight(c);
	b->lChild = a; a->parent = b;
	b->rChild = c; c->parent = b; updateHeight(b);
	return b;  // The new root node of the subtree
}
template<typename T> BinNodePosi(T) BST<T>::rotateAt(BinNodePosi(T) v) {
	BinNodePosi(T) p = v->parent, g = p->parent; // Father, grandfather
	if (isLChild(*p))// zig
		if (IsLChild(*v)) {// Zig Zig (both V and P are left children)
			p->parent = g->parent;// Upward connection
			return connect34(v, p, g,
				v->lChild, v->rChild, p->rChild, g->rChild);
		}
		else {// Zig zag (P is the left child, v is the right subtree)
			v->parent = g->parent; //Upward connection
			return connect34(p, v, g,
				p->lChild, v->lChild, v->rChild, g->rChild);
		}
	else { /* ..zag-zig & zag-zag ..*/}
}

The height and number of nodes of the binary search tree satisfy h = O(n), and the worst time complexity of searching on it is O(n);
n and h of the balanced binary search tree satisfy the relationship h = O(lgn), and the worst time complexity of searching on it is O(lgn).

Chapter 10 advanced search tree

P268
Extended tree locality

A1 stretch tree: stretch layer by layer


Transfer the newly visited node to the tree root

Climb up step by step: single rotation layer by layer

A2 stretch tree: double layer stretch

Go up two layers, not one
Repeatedly examine the three generations of ancestors and grandchildren
Rotate twice and rise two layers

For Zig zig and zag zag

Layer by layer:

double-deck

After the double-layer extension, the tree height is reduced to half of the original
Path folding
Allocation O(logn)

A3 extended tree: algorithm implementation

P282

/* Extended tree interface */
template <typename T>
class Splay : public BST<T> {// Derived from BST
protected: BinNodePosi(T) splay(BinNodePosi(T) v);  // Extend v to the root
public:
	BinNodePosi(T)& search(const T& e);  // lookup
	BinNodePosi(T) insert(const T& e);   // insert
	bool remove(const T& e);  // delete
};
/* Stretch algorithm */
template <typename T> BinNodePosi(T) Splay<T>::splay(BinNodePosi(T) v) {
	if (!v) return NULL; BinNodePosi(T) p; BinNodePosi(T) g; // Father, grandfather
	while ((p = v->parent) && (g = p->parent)) {// From bottom to top, repeated double-layer stretching
		BinNodePosi(T) gg = g->parent; // After each round. v will be fathered by his great grandfather
		if( IsLChild( * v))
			if (IsLChild(*p)) { /* zig-zig*/ }
			else {/*zig-zag*/}
		else if (IsRChild(*p)) {/*zag-zag*/ }
		else {/*zag-zig*/}
		if (!gg) v->parent = NULL;  // If there is no great grandfather gg, v is now the root of the tree; Otherwise, after that, v should be left or right
		else(g == gg->lc) ? attachAsLChild(gg, v) : attachAsRChild(gg, v); // children
		updateHeight(g); updateHeight(gg, v); updateHeight(v);
	}// At the end of double-layer stretching, there must be g==NULL, but p may not be empty
	if (p = v->parent) {/*If p is really a root, it only needs another single spin (at least one more time)*/ }
	v->parent = NULL; return v;  // Stretch out and v reach the root of the tree
}
if (IsLChild(*v)) {
	if (IsLChild(*p)) {// zig-zig
		attachAsLChild(g, p->rc);
		attachAsLChild(p, v->rc);
		attachAsRChild(p, g);
		attachAsRChild(v, p);
}else { /* zig-zag*/}
}
else {
	if (IsRChild(*p)) { /* zag-zag */}
	else { /* zag-zig*/}
}
/* lookup */
template <typename T> BinNodePosi(T)& Splay<T>::search(const T& e) {
	// Call the internal interface of the standard BST to locate the target node
	BinNodePosi(T) p = searchIn(_root, e, _hot = NULL);
	// Whether successful or not, the last visited node will extend to the root
	_root = splay(p ? p : _hot);  // Success, failure
	// Always return the root node
	return _root;
}

Locality
Insert:

Delete:

Comprehensive evaluation:

B1 B-tree: big data

P289
Achieve efficient I/O

640k ought to be enough for anybody.
—B.Gates, 1981

Cache

Batch access

B2 B-tree: structure

P295
Wider, shorter
Balanced multiple search tree
P297
2-4 red black trees
Nodes: vectors, sequences

/* BTNode*/
template <typename T> struct BTNode {// B-tree node
	BTNodePosi(T) parent;  // father
	Vector<T> key;   // Numerical vector
	Vector < BTNodePosi(T) > child;  // Child vector (its length is always 1 more than key)
	BTNode() { parent = NULL; child.insert(0, NULL); }
	BTNode(T e, BTNodePosi(T) lc = NULL, BTNodePosi(T) rc = NULL) {
		parent = NULL;  // As the root node, and initially
		key.insert(0, e); // Only one key, and
		child.insert(0, lc);   child.insert(1, rc);   // Two children
		if (lc)  lc->parent = this; if (rc) rc->parent = this;
	}
};

/* Two vectors */
/* BT tree*/

#Define btnodeposi (T) btnode < T > * / / B-tree node location
template <typename T> class BTree {// B-Trees 
protected:
	int _size;  int _order; BTNodePosi(T) _root;  // Total number, order and root of keys
	BTNodePosi(T) _hot;  // search() the last non empty node accessed (auxiliary dynamic operation)
	void solveOverflow( BTNodePosi(T) );   // Split processing after overflow due to insertion
	void solveUnderflow( BTNodePosi(T) );  // Consolidation after overflow due to deletion
public:
	BTNodePosi(T) search(const T & e);  // lookup
	bool insert(const T& e);  // insert
	bool remove(const T& e);   // delete
};

Fewer layers of B-tree help reduce the number of I/O

B3 B-trees: finding

P303
One read I/O operation
One time sequential search
The failed search must terminate at the external node

/* lookup */
template <typename T> BTNodePosi(T) BTree<T>::search(const T& e) {
	BTNodePosi(T) v = _root; _hot = NULL;   // Starting from the root node
	while (v) {// Find layer by layer
		Rank r = v->key.search(e);    // Search in sequence in the vector corresponding to the current node
		if (0 <= r && e == v->key[r])  return v;  // If successful, return; otherwise
		_hot = v; v = v->child[r + 1];    // Go along the reference to the corresponding lower subtree and load its root I/O
	}//Ruoyin! v and exit, it means reaching the external node
	return NULL;  // fail
}

Height of tree h

N success cases, N+1 failure cases.

B4 B-tree: inserting

P309

/* insert */
template <typename T>
bool BTree<T>::insert(const T& e) {
	BTNodePosi(T) v = serach(e);
	if (v)  return false;  // Confirm that e does not exist
	Rank r = _hot->key.search(e);  // Determine the insertion position in node - hot
	_hot->key.insert(r + 1, e);   // Insert the key into the corresponding position
	_hot->child.insert(r + 2, NULL);   // Create a null pointer
	_size++;   solveOverflow(_hot);    // If overflow occurs, it needs to be split
	return true;   // Insert successful
}


T= O(h)

B5 B-trees: deleting

P315

/* delete  */
template <typename T>
bool BTree<T>::remove(const T& e) {
	BTNodePosi(T) v = search( e );
	if ( ! v ) return false;   // Confirm e exists
	Rank r = v->key.search(e);   // Confirm the rank of e in v
	if (v->child[0]) {// If v not leaves
		BTNodePosi(T) u = v->child[r + 1];  // Go straight to the left in the right subtree
		while (u->child[0])  u = u->child[0];  // Find the successor of e (must belong to a leaf node)
		v->key[r] = u->key[0]; v = u; r = 0;   // And exchange positions with it
	}// So far, v must be at the bottom, and the r key is the one to be deleted
	v->key.remove( r ); v->child.remove( r + 1 ); _size--;
	solveUnderflow(v); return true;    // Rotate or merge if necessary
}

If you look left and right and cannot rotate, consider merging

The reduction of B-tree height only occurs when two children of the root node merge.

1. The time spent in the insert operation mainly comes from search(e)

  • In the B-tree, the time cost of cross node access (I/O operation) is much greater than that of sequential access within the node

2. The time spent in the deletion operation mainly comes from the time spent in search(e) and searching for subsequent nodes. The subsequent search needs to be cross node. Deleting is more time-consuming
In the worst case, the two costs are equal.

C1 red black tree: motivation

P319

Version number
Consistency structure / persistence
P321 relevance
Share a lot and update a little
P322

C2 red black tree: structure

P323

Red black tree is a special case of BBST

/* Red black tree interface definition */

template <typename T> class RedBlack : public BST<T> {//Red black tree
public: // BST::search() and other interfaces can be used directly
	BinNodePosi(T) insert(const T& e);  // insert
	bool remove(const T& e);   // delete
protected:
	void solveDoubleRed(BinNodePosi(T) x);  // Double red correction
	void solveDoubleBlack(BinNodePosi(T) x);  // Double black correction
	int updateHeight(BinNodePosi(T) x);   // Update the height of node x, black height
};
template <typename T> int RedBlack<T>::updateHeight(BinNodePosi(T) x) {
	x->height = max(stature(x->lc), stature(x->rc));
	if (IsBlack(x))  x->height++; return x->height;   // Count only black nodes
}
C3 red black tree: Insert

P330

/* Treatment of double red defect */

template <typename T> BinNodePosi(T) RedBlack<T>::insert(const T& e) {
	// Confirm that the target node does not exist
	BinNodePosi(T)& x = serach(e); if (x) return x;
	// Create a red node x to_ hot as father, black height - 1
	x = new BinNode<T>(e, _hot, NULL, -1); _size++;

	// If necessary, double red correction is required
	solveDoubleRed(x);

	// Returns the inserted node
	return x ? x : _hot->parent;
}// Whether e exists in the original tree or not, there is always X - > data = = e when returning





C4 red black tree: delete

Double Black: the deleted points are black, resulting in the number of black nodes on some paths being reduced by 1
BB-1

BB- 2B

BB- 3

summary

Supplementary notes

1. The worst-case time complexity of a single lookup operation of an extended tree is greater than that of an AVL tree
2,

Access of large-scale data (not all stored in memory): B-tree
It is easy to implement, and the allocation complexity of each interface is O(lgn): extended tree
Dealing with geometry related problems: kd tree
After expansion, it can support access to historical versions: red black tree

P11 dictionary

P345

A hash

Service telephone

Hashing: translation hash

Bucket: bucket
Bucket array bucket array / hash table

B hash function

P351
Hash function:
1. OK: the same key is always mapped to the same address
2. Fast
3. Full shot
4. Uniform and avoid aggregation

1. Residual method

  • hash(key) = key % M
  • When M is a prime number, the data covers the hash table most fully and distributes most evenly

Defects of residual method:

  1. Fixed point: always have hash(0) = 0
  2. Zero order uniform: [0,R) keys are evenly distributed to M buckets; however, the hash addresses of adjacent keys must also be adjacent

First order uniformity: adjacent keys, hash addresses are no longer adjacent

2. MAD method

3. Digital analysis
4. Square median


Reason for taking the middle number: the middle number is accumulated by more digits
5. Folding method

6. Exclusive or method

  • The implementation of pseudo-random number generator will be different due to different platforms, and the portability is poor

P359

/* Polynomial method: applicable to English strings */
static size_t hashCode(char s[]) {//Approximate polynomial
	int h = 0;
	for (size_t n = strlen(s), i = 0; i < n; i++) {
		h = (h << 5) | (h >> 27);
		h += (int)s[i];
	}
	return (size_t) h;
}
C conflict resolution (1) (2)

P362
Dynamic maintenance, hash conflict is inevitable

1. Multi slot position

2. Independent chain

open addressing / closed hashing

3. Linear probing: in case of conflict, probe the adjacent bucket unit

lazy removal: only the deletion mark is made, and the search chain does not need to be continued
Insufficient linear test: the test distance is too close

4. Square probing

  • It can alleviate the aggregation phenomenon

5. Bidirectional square test
M prime = 4k + 3

Double square theorem (Fermat): any prime number p can be expressed as the sum of squares of a pair of integers if and only if p% 4 = 1


D-bucket sorting

P375
distribution

F count sort

P377

The value range of N elements to be sorted is [1,M], and the time complexity of counting sorting is O(M+N)

Supplementary notes:

1. Using open addressing to resolve conflicts, the actual position of the entry is not necessarily the corresponding hash function value

Chapter 12 priority queue

P378

A1 needs and motivations

Night clinic
Multitask scheduling

Priority based access

/* Priority queue */
template <typename T> struct PQ {  // priority queue
	virtual void insert(T) = 0;   // Insert entries in order of priority
	virtual T getMax() = 0;   //Take out the entry with the highest priority
	virtual T delMax() = 0;   // Delete the entry with the highest priority
};// Priority queue: abstract data type


A2 basic implementation

P381
Balance cost and efficiency
Vector:

Sorted Vector



Extreme element, partial order

B1 complete binary reactor: structure

P384

complete Binary Tree: AVL with non negative balance factor everywhere

Logically, it is equivalent to a complete binary tree
Physically, it is realized directly with the help of vectors

Complete Binary Tree

#define Parent(i) ( (i-1) >> 1)
# Define lchild (I) (1 + ((I) < 1)) / / odd
#Define rchild (I) ((1 + (I)) < 1) / / even

multiple inheritance

/* PQ_ComplHeap = PQ + Vector*/

template <typename T> class PQ_ComlHeap : public PQ<T>, public Vector<T> {
protected: Rank percolateDown(Rank n, Rank i);   // Lower filtration
		 Rank percolateUp(Rank i);  // Upper filtration
		 void heapify(Rank n);   // Floyd heap building algorithm
public:
	PQ_ComplHeap(T* A, Rank n)// Batch construction
	{
		copyFrom(A, 0, n); heapiffy(n);
	}
	void insert(T);   // Insert entries according to the priority determined by the comparator
	T getMax() { return _elem[0];   }  // Read the entry with the highest priority
	T delMax();    // Delete the entry with the highest priority
};

Heap ordering

/* H[0] That is, the global maximum element */

template <typename T> T
PQ_ComplHeap<T>::getMax() { return _elem[0]; }
B2 complete binary reactor: insert and percolate up

P388
Insert entry e: take e as the last element to access the vector (which may destroy heap ordering), and then filter up

/* Heap insert */
template <typename T> void PQ_ComplHeap<T>::insert(T e)// insert
{
	Vector<T> ::insert(e);   percolateUp(_size - 1);
}

template <typename T>// Filter up the ith entry, i <_ size
Rank  PQ_ComplHeap<T>::percolateUp(Rank i) {
	while ( ParentValid(i) ) { // As long as i there is a father (who has not yet reached the top of the pile), then
		Rank j = Parent(i);   // Record the father of i as j
		if (lt(_elem[i], _elem[j]))  break;   // Once the father and son are no longer in reverse order, the upper filtering is completed
		swap(_elem[i], _elem[j]);  i = j;     // Otherwise, swap parent-child positions and go up one level
	}// while
	return i;   // Return to the final position reached by the upper filter
}

h = O(logn)
swap: 3 exchanges.

Improvement: first back up e, determine the location, and then make an exchange

B3 complete binary stack: deletion and downward filtering

P392
Structure and stacking order

The last element is replaced and then filtered down

/* Heaps: deleting*/

template <typename T> T PQ_ComplHeap<T>::delMax() {// delete
	T maxElem = _elem[0]; _elem[0] = _elem[--_size];   // Remove the top of the pile and replace it with the last entry
	percolateDown(_size, 0);  // Perform down filtration on the new reactor top
	return maxElem;   // Returns the maximum entries previously backed up
}

template <typename T>// Filter down the ith of the first n entries, i < n
Rank PQ_ComplHeap<T>::percolateDown(Rank n, Rank i) {
	Rank j;   // father
	while (i != (j = ProperParent(_elem, n, i)))  // As long as i is not j, then
	{
		swap(_elem[i], _elem[j]); i = j;   // Transposition and continue to investigate i
	}
	return i;   // Return to the position reached by the lower filter
}

Efficiency:
compare
Swap 3logn

B4 complete binary reactor: batch reactor construction

P396
Heapification

1. Top down top filtration

/* Top down top filtration */

PQ_ComplHeap(T* A, Rank n) { coptFrom(A, 0, n);  heapify(n); }

template <typename T> void PQ_ComplHeap<T>::heapify(Rank n) {
	for (int i = 1, i < n; i++)   // When traversing a node according to the hierarchy, there is no need to filter up, so start from 1
		percolateUp(i);   // Insert each node through upper filter
}

T(n) = nlogn
2. Bottom up filtering
Partial order
Merge small piles into large piles
delMax()

template <typename T>
void PQ_ComplHeap<T>::heapify(Rank n) {// 
	for (int i = LastInternal(n); i >= 0; i--)   // From bottom to top, from right to left, in turn
		percolateDown(n, i);   // Filter down internal nodes
}  // Layer by layer merging of sub heaps


The closer to the bottom layer, the more nodes (brute force method)

C heap sort

P401


Local O(1)

Exchange + lower filtration

/* Heap sort */
template <typename T> // Sort vectors [lo,hi) in place
void Vector<T>::heapSort(Rank lo, Rank hi) {
	PQ_ComplHeap<T> H(_elem + lo, hi - lo);  // Heap building in the interval to be sorted, O(n)
	while (!H.empty()) // Repeatedly remove the largest element and put it into the sorted suffix until the heap is empty
		_elem[--hi] = H.delMax();    // Equivalent to the lower filtration after the exchange between the top and the end elements
}

Heap sorting: continuously call delMax() after creating a heap

F1 & 2 left stack: structure

P405

In order to effectively complete heap merging

Null node path length Null Path Length

  • Introduce all external nodes: eliminate the one degree node and turn it into a true binary tree

Height of node

F3 left heap: merge algorithm

P411

/* LeftHeap */
template <typename T> // Based on binary tree, the priority queue is implemented in the form of left heap
class PQ_LeftHeap : public PQ<T>, punlic BinTree<T> {
public:
	void insert(T);  // Insert elements (in the order of priority determined by the comparator)
	T getMax() { return _root->data;  }  // Take out the element with the highest priority
	T delMax();     // Delete the element with the highest priority
};

template <typename T>
static BinNodePosi(T) merge(BinNodePosi(T), BinNodePosi(T));

The left heap does not meet the structural requirements, and the physical structure is no longer compact. It can be derived from the tree structure

/* Left heap: merging*/

template <typename T>
static BinNodePosi(T) merge(BinNodePosi(T) a, BinNodePosi(T) b) {
	if (!a)  return b;   // Recursive basis
	if (!b)  return a;   // Recursive basis
	if (lt(a->data, b->data))   swap(b, a);    // General situation: first, make sure that b is not large and a is large
	a->rc = merge(a->rc, b);    // Merge the right sub heap of a with b
	a->rc->parent = a;  // Update parent-child relationship
	if (!a->lc || a->lc->npl  <   a->rc->npl)  // If necessary
		swap(a->lc, a->rc);   // Swap the left and right sub heaps of a to ensure that the npl of the right sub heap is not large
	a->npl = a->rc ? a->rc->npl + 1 : 1;   // Update npl of a
	return a;   // Returns the merged heap top
}


F4 left heap: insert + delete

P415
Insert is merge

/* insert*/

template <typename T>
void PQ_LeftHeap<T>::insert(T e) { // O(logn)
	BinNodePosi(T) v = new BinNode<T>(e);   // Create a binary tree node for e
	_root = merge(_root, v);   // The insertion of new nodes is completed by merging
	_root->parent = NULL;    // 
	_size++;   //Renewal scale
}

Delete:

/* delMax() */
template <typename T> T PQ_LeftHeap<T>::delMax() {// O(logn)
	BinNodePosi(T) lHeap = _root->lc;   // Zuo zidui
	BinNodePosi(T)  rHeap = _root->rc;   // Right sub heap
	T e = _root->data;  // Maximum element at the top of the backup heap

	delete _root; _size--;   // Delete root node
	_root = merge(lHeap, rHeap);   // Merge the original left and right sub heaps
	if (_root)  _root->parent = NULL;   // Update parent-child connections
	return e;   // Returns the data item of the original root node
}

Supplementary notes

1. The insert, getmax and delmax interfaces of priority queue realized by balanced binary search tree can achieve the time complexity of O(lgn).

Chapter XIII string

P417

A. ADT

The string is structurally equivalent to a Vector vector

B. Pattern matching

String matching
Text string T, n; Mode string P, m
1. Brute force matching
Move the mode string from left to right in character

/* Brute force matching: Version 1*/
int match(char* P, char* T) {
	size_t n = strlen(T), i = 0;
	size_t m = strlen(P), j = 0;
	while( j < m && i < n)//Match characters one by one from left to right
		if (T[i] == T[j]) { i++; j++; }  // If it matches, go to the next pair of characters
		else { i -= j - 1;  j = 0; }   // Otherwise, T fallback and P reset
	return i - j;
}
/* Brute force matching: version 2*/

int match(char* P, char* T) {
	size_t  n = strlen(T), i = 0;   // Align T[i] with P[0]
	size_t  m = strlen(P), j;   // T[i+j] aligned with P[j]
	for (i = 0; i < n - m + 1; i++) { // T from the i th character
		for (j = 0; j < m; j++)// Corresponding characters in P
			if (T[i + j] != P[j])   break;  //In case of mismatch, P moves one character to the right as a whole and compares again
		if (m <= j)  break;    // Matching string found
	}
	return i;  
}

Best case: O(m)
Worst case: O(n) × m)

C1. KMP algorithm: memory method

P426
Worst O(n)

The reason for the inefficiency of brute force method: after T fallback and P reset, the previously compared characters will participate in the comparison again.

? According to the P-scan T, the mark can match, and then judge. How about it

C2. KMP algorithm: query table

P430
Make adequate plans

/* KMP Main algorithm */  /* Modified on the basis of version 1 of brute force method*/
int match(char* P, char* T) {
	int* next = buildNext(p);   // Construct next table
	int n = (int)strlen(T), i = 0;  // Text string pointer
	int m = (int)strlen(P), j = 0;   // Mode string pointer
	while( j < m && i < n)// Compare characters one by one from left to right
		if (0 > j || T[i] == P[j]) {// If match
			i++; j++; // Hand in hand
		}
		else// Otherwise, P moves to the right and T does not retreat
			j = next[j];
	delete[] next;  // Release next table
	return i - j;   
}

C3. KMP algorithm: next [] table

P433
Self matching = fast shift right
Longest self matching = fast shift right + avoid fallback

Next[0] = -1, sentry
Virtual experiment

C4. KMP algorithm: construct next [] table

P436
Recurrence
next[j]: in P[0,j), the length of the maximum self matching true prefix and true suffix.

Self similarity of prefix

/* next[]surface*/

int* buildNext(char* P) {// Construct the netx [] table of pattern string P to match the pattern string itself
	size_t m = strlen(P), j = 0;  // Main string pointer
	int* N = new int(m);    // next table
	int t = N[0] = -1;   // Mode string pointer (P[-1] wildcard)
	while (j < m - 1)
		if (0 > t || P[j] == P[t])  // matching
			N[++j] = ++t;
		else // Mismatch
			t = N[t];
	return N;
}

C5. KMP algorithm: Allocation Analysis

P439
O(n)

For text strings with length N and pattern strings with length m, the time complexity of KMP algorithm is O(m + n)

C6. KMP algorithm: further improvement

P441
court defeat by fighting against overwhelming odds
next query table control program process

/* next Table: improved version*/
int* buildNext(char* P) {
	size_t m = strlen(P), j = 0;   // Main string pointer
	int* N = new int[m];  // next table
	int t = N[0] = -1; // Mode string pointer

	while( j < m-1)
		if (0 > t || P[j] == P[t]) {//matching
			j++; t++; N[j] = P[j] != P[t] ? t : N[t];
		}
		else// Mismatch
			t = N[t];
	return N;
}

Binary string: KMP

D. BM_BC algorithm

D1. BM_BC algorithm: start with the end
P446
Bad character strategy

Focus on failed comparisons
Let greater failures be exposed earlier

D2. BM_BC algorithm: bad characters
P450
Choose one to avoid backtracking

sentry

D3. BM_BC algorithm: construct bc []
P452

/* Construction bc[] table */
int* buildBC(char* P) {
	int* bc = new int[256];   // bc [] table, longer than the alphabet
	for (size_t j = 0; j < 256; j++)  bc[j] = -1;  //Initialization (uniformly pointing to wildcards)
	for (size_t m = strlen(P), j = 0; j < m; j++)// Scan from left to right
		bc[P[j]] = j;    //Refresh the occurrence position record of P[j] (painter algorithm: later overwrite the past)
	return bc;
}// The second loop avoids calling strlen() repeatedly by referring to the temporary variable m

D4. BM_BC algorithm: Performance Analysis
P454
Best case O(n/m)


Worst case scenario
O(n×m)

E: BM_GS algorithm

P455
good-suffix

Experience = matching suffix
Among all prefixes P[0,t), take the longest one matching the suffix of U



F: Karp Rabin algorithm

P461
A string is an integer
Everything counts
Hash

Modular complementary function
Exclude in O(1) time

Supplementary notes:

1. The return values of brute force string matching method when matching is successful / failed are: the first position of P in T / a number greater than n-m

P14 sorting

P468

Quick sort

divide and rule

Quick sort: the process of converting all elements to pivot points one by one.


instable
Local

Average performance

a4: quick sort variant

P476

/* Quick sort */

template <typename T> void Vector<T>::quickSort(Rank lo, Rank hi) {
	if (hi - lo < 2)   return;    // Single element intervals are naturally ordered, otherwise
	Rank mi = partition(lo, hi - 1);   // Construct pivot point first
	quickSort(lo, mi);   // Prefix sorting
	quickSort(mi + 1, hi);   // Suffix sorting
}

template <typename T> Rank Vector<T>::partition(Rank lo, Rank hi) { //[lo, hi]
	swap(_elem[lo], elem[lo + rand() % (hi - lo + 1)]);   // Random exchange

	T pivot = _elem[lo]; int mi = lo;
	for (int k = lo + 1; k <= hi; k++)// Examine each [k] from left to right
		if (_lem[k] < pivot)  // If [k] is less than the axis point, then
			swap(_elem[++mi], _elem[k]);   // Swap with [mi], L extends to the right
	swap(_elem[lo], _elem[mi]);   // Candidate pivot point homing
	return mi;   // Returns the rank of the pivot point         
}

instable

selection

The k-th largest element
Centered element

Select mode

P481
median

Traverse + count + take extreme value

template <typename T> bool majority(Vector<T> A, T& maj)
{
	return majEleCheck(A, maj = median(A));
}
template <typename T> bool najiority(Vector<T> A, T& maj)
{
	return majElecheck(A, maj = mode(A));  
}

template <typename T> bool majority(Vector<T> A, T& maj)
{
	return majEleCheck(A, maj = majEleCandidate(A));
}

Reduce and govern

template <typename T> T majEleCandidate(Vector<T> A) {
	T maj;   //Mode candidate
	// Linear scanning: with the help of counter c, the difference between the number of maj and other elements is recorded
	for(int c=0; i < A.size(); i++)
		if (0 == c) {// Whenever c returns to zero, it means that the prefix p can be cut off
			maj = A[i]; c = 1; // The mode candidate is changed to the new current element
		}
		else// otherwise
			maj == A[i] ? c++ : c--;   // Update the difference counter accordingly
	return maj;  // 
}

quickSelect
P486

/* quickSelect()algorithm */
template <typename T> void quickSelect(Vector<T>& A, Rank k) {
	for (Rank lo = 0, hi = A.size() - 1; lo < hi;) {
		Rank i = lo, j = hi; T pivot = A[lo];
		while (i < j) {// O(hi - lo + 1) = O(n)
			while (i < j && pivot <= A[j]) j--;   A[i] = A[j];
			while (i < j && A[i] <= pivot) i++; A[j] = A[i];
		}
		A[i] = pivot;
		if (k <= i)  hi = i - 1;
		if (i <= k)   lo = i + 1;
	}
}

Select the median from the vector with scale n, and the worst time complexity of quickselect algorithm is O(n^2)

linearSelect() algorithm
P487

Shell Sort

P491

1959

Matrix rearrangement: logically sort (call by rank)

Insert sort

Step sequence

Mutual prime

Insertion sort is sensitive to the order of the input sequence

Postage problem
Collect postage

linear combination

Theorem K (knuth)
Any sequence that was originally g-ordered will remain g-ordered after h-sorting.

The linear combination of ordered sequences is still ordered.


Insertion sort: the total number of inverse sequences will continue to decrease. The input is sensitive and linearly proportional to the number of inverse pairs.

Stability: Merge
Unstable: fast, heap, Hill

The median selection algorithm of linear time is very inefficient.

Prevent quick sort from becoming inefficient due to unbalanced pivot points:
Method 1: choose the middle of the three
Method 2: combine heap sort with quick sort

Topics: C++ data structure CS