Skip to content

Array Slicing

Represent a contiguous subrange of an array as either a view or a copied array.

Array slicing selects a contiguous subrange from an array. A slice may be a view into the original storage, or it may be a new copied array.

You use it when an algorithm should operate on part of an array without manually passing start and end indices everywhere.

Problem

Given an array AA of length nn, create a slice from index ll up to but not including index rr:

A[l:r] A[l:r]

where

0lrn 0 \le l \le r \le n

Structure

A slice view usually stores:

  • reference to the same backing array
  • start offset
  • length

For example:

A=[8,3,5,1,9] A = [8, 3, 5, 1, 9]

and

A[1:4]=[3,5,1] A[1:4] = [3, 5, 1]

The slice view points into the same storage rather than copying elements.

Algorithm

Create a slice view:

slice(A, l, r):
    if l < 0 or r > length(A) or l > r:
        error "invalid slice range"

    S.base = A
    S.start = l
    S.length = r - l
    return S

Read from a slice:

get(S, i):
    if i < 0 or i >= S.length:
        error "index out of bounds"

    return S.base[S.start + i]

Example

Let

A=[8,3,5,1,9] A = [8, 3, 5, 1, 9]

Create:

S=A[1:4] S = A[1:4]
slice indexbase indexvalue
013
125
231

Reading S[2]S[2] maps to:

A[1+2]=A[3]=1 A[1 + 2] = A[3] = 1

Return 11.

Correctness

The slice range check ensures that ll and rr describe a valid contiguous subrange of the base array. The slice stores length rlr - l, so every valid slice index ii satisfies 0i<rl0 \le i < r - l. Mapping it to l+il + i gives an index in the original range [l,r)[l, r), so each slice access returns exactly the corresponding base-array element.

Complexity

operationview slicecopied slice
create sliceO(1)O(1)O(k)O(k)
getO(1)O(1)O(1)O(1)
setO(1)O(1)O(1)O(1)

Here:

k=rl k = r - l

A view slice uses:

O(1) O(1)

extra space.

A copied slice uses:

O(k) O(k)

extra space.

When to Use

Array slicing is appropriate when:

  • an algorithm works on subranges
  • copying would be unnecessary
  • start and end indices make code noisy
  • recursive or divide-and-conquer logic needs clean boundaries

Be careful with view slices when:

  • mutating the slice may affect the original array
  • a small slice keeps a large backing array alive
  • concurrent code may observe shared storage

Implementation

class Slice:
    def __init__(self, base, start, end):
        if start < 0 or end > len(base) or start > end:
            raise IndexError("invalid slice range")
        self.base = base
        self.start = start
        self.length = end - start

    def get(self, i):
        if i < 0 or i >= self.length:
            raise IndexError("index out of bounds")
        return self.base[self.start + i]

    def set(self, i, x):
        if i < 0 or i >= self.length:
            raise IndexError("index out of bounds")
        self.base[self.start + i] = x
type Slice[T any] struct {
    base  []T
    start int
    size  int
}

func NewSlice[T any](base []T, start, end int) (*Slice[T], bool) {
    if start < 0 || end > len(base) || start > end {
        return nil, false
    }

    return &Slice[T]{
        base:  base,
        start: start,
        size:  end - start,
    }, true
}

func (s *Slice[T]) Get(i int) (T, bool) {
    var zero T
    if i < 0 || i >= s.size {
        return zero, false
    }
    return s.base[s.start+i], true
}

func (s *Slice[T]) Set(i int, x T) bool {
    if i < 0 || i >= s.size {
        return false
    }
    s.base[s.start+i] = x
    return true
}